summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES459
-rw-r--r--Documentation/Makefile.in4
-rw-r--r--Documentation/btrfs-balance.asciidoc16
-rw-r--r--Documentation/btrfs-check.asciidoc12
-rw-r--r--Documentation/btrfs-convert.asciidoc9
-rw-r--r--Documentation/btrfs-debug-tree.asciidoc38
-rw-r--r--Documentation/btrfs-device.asciidoc10
-rw-r--r--Documentation/btrfs-filesystem.asciidoc61
-rw-r--r--Documentation/btrfs-inspect-internal.asciidoc82
-rw-r--r--Documentation/btrfs-man5.asciidoc32
-rw-r--r--Documentation/btrfs-qgroup.asciidoc2
-rw-r--r--Documentation/btrfs-receive.asciidoc2
-rw-r--r--Documentation/btrfs-replace.asciidoc6
-rw-r--r--Documentation/btrfs-scrub.asciidoc6
-rw-r--r--Documentation/btrfs-show-super.asciidoc54
-rw-r--r--Documentation/btrfs-subvolume.asciidoc2
-rw-r--r--Documentation/btrfs.asciidoc2
-rw-r--r--Documentation/btrfstune.asciidoc2
-rw-r--r--Documentation/mkfs.btrfs.asciidoc10
-rw-r--r--Makefile.in22
-rw-r--r--backref.c8
-rw-r--r--btrfs-calc-size.c484
-rw-r--r--btrfs-completion8
-rw-r--r--btrfs-convert.c12
-rw-r--r--btrfs-corrupt-block.c17
-rw-r--r--btrfs-debug-tree.c439
-rwxr-xr-xbtrfs-debugfs117
-rw-r--r--btrfs-find-root.c5
-rw-r--r--btrfs-fragments.c12
-rw-r--r--btrfs-image.c49
-rw-r--r--btrfs-map-logical.c13
-rw-r--r--btrfs-select-super.c18
-rw-r--r--btrfs-show-super.c520
-rw-r--r--btrfs-zero-log.c14
-rw-r--r--btrfstune.c3
-rw-r--r--chunk-recover.c18
-rw-r--r--cmds-balance.c83
-rw-r--r--cmds-check.c532
-rw-r--r--cmds-device.c90
-rw-r--r--cmds-fi-du.c580
-rw-r--r--cmds-fi-du.h23
-rw-r--r--cmds-filesystem.c62
-rw-r--r--cmds-inspect-dump-super.c544
-rw-r--r--cmds-inspect-dump-super.h26
-rw-r--r--cmds-inspect-dump-tree.c551
-rw-r--r--cmds-inspect-dump-tree.h24
-rw-r--r--cmds-inspect-tree-stats.c506
-rw-r--r--cmds-inspect-tree-stats.h26
-rw-r--r--cmds-inspect.c43
-rw-r--r--cmds-property.c34
-rw-r--r--cmds-qgroup.c84
-rw-r--r--cmds-quota.c14
-rw-r--r--cmds-rescue.c8
-rw-r--r--cmds-restore.c90
-rw-r--r--cmds-scrub.c6
-rw-r--r--cmds-send.c37
-rw-r--r--cmds-subvolume.c130
-rwxr-xr-xconfigure36
-rw-r--r--configure.ac2
-rw-r--r--ctree.c7
-rw-r--r--ctree.h12
-rw-r--r--debian/changelog16
-rw-r--r--debian/control3
-rw-r--r--disk-io.c61
-rw-r--r--disk-io.h4
-rw-r--r--extent-tree.c22
-rw-r--r--extent_io.c3
-rw-r--r--file-item.c1
-rw-r--r--find-root.c12
-rw-r--r--free-space-cache.c1
-rw-r--r--inode-map.c1
-rw-r--r--internal.h42
-rw-r--r--ioctl.h15
-rw-r--r--kerncompat.h20
-rw-r--r--mkfs.c18
-rw-r--r--print-tree.c30
-rw-r--r--qgroup-verify.c51
-rw-r--r--qgroup-verify.h2
-rwxr-xr-xtests/cli-tests.sh41
-rwxr-xr-xtests/cli-tests/001-btrfs/test.sh15
-rwxr-xr-xtests/cli-tests/002-balance-full-no-filters/test.sh21
-rw-r--r--tests/common26
-rwxr-xr-x[-rw-r--r--]tests/convert-tests.sh90
-rw-r--r--tests/fuzz-tests/images/bko-96971-btrfs-image.raw.xzbin0 -> 6448 bytes
-rw-r--r--tests/fuzz-tests/images/bko-96971-btrfs-image.txt69
-rwxr-xr-xtests/misc-tests/013-subvolume-sync-crash/test.sh49
-rwxr-xr-xtests/misc-tests/014-filesystem-label/test.sh69
-rwxr-xr-xtests/mkfs-tests/001-basic-profiles/test.sh2
-rwxr-xr-xtests/mkfs-tests/005-long-device-name-for-ssd/test.sh3
-rw-r--r--utils.c298
-rw-r--r--utils.h42
-rwxr-xr-xversion.sh2
-rw-r--r--volumes.c2
93 files changed, 4685 insertions, 2464 deletions
diff --git a/CHANGES b/CHANGES
new file mode 100644
index 00000000..6186808d
--- /dev/null
+++ b/CHANGES
@@ -0,0 +1,459 @@
+btrfs-progs-4.5.2 (2016-05-02)
+ * new/moved command: btrfs-calc-stats -> btrfs inspect tree-stats
+ * check: fix false alert for metadata blocks crossing stripe boundary
+ * 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
+ * dev ready: accept only one argument for device
+ * dev stats: print "devid:N" for a missing device instead of "(null)"
+ * other:
+ * lowest supported version of e2fsprogs is 1.41
+ * minor cleanups, test updates
+
+btrfs-progs-4.5.1 (2016-03-31)
+ * mkfs: allow DUP on multi-device filesystems
+ * bugfixes: build fixes, assorted other fixes
+
+btrfs-progs-4.5 (2016-03-20)
+ New/moved commands:
+ * btrfs-show-super -> btrfs inspect-internal dump-super
+ * btrfs-debug-tree -> btrfs inspect-internal dump-tree
+
+ New commands:
+ * btrfs fi du - calculate disk usage, including shared extents
+
+ Enhancements:
+ * device delete - delete by id (needs kernel support, not merged to
+ 4.6)
+ * check - new option to specify chunk root
+ * debug-tree/dump-tree - option -t understands human readable name of
+ the tree (along numerical ids)
+ * btrfs-debugfs - can dump block group information
+
+ Bugfixes:
+ * all commands should accept the option separator "--"
+ * several fixes in device scan
+ * restore works on filesystems with sectorsize > 4k
+ * debug-tree/dump-tree - print compression type as string
+ * subvol sync: fix crash, memory corruption
+ * argument parsing fixes: subvol get-default, qgroup create/destroy/
+ assign, inspect subvolid-resolve
+ * check for block device or regular file in several commands
+
+ Other:
+ * documentation updates
+ * manual pages for the moved tools now point to btrfs-filesystem
+ * testsuite updates
+
+btrfs-progs-4.4.1 (2016-02-26)
+ * find-root: don't skip the first chunk
+ * free-space-tree compat bits fix
+ * build: target symlinks
+ * documentation updates
+ * test updates
+
+btrfs-progs-4.4 (2016-01-18)
+ User visible changes:
+ * mkfs.btrfs --data dup
+
+ People asked about duplicating data on a single device for a long time. There
+ are no technical obstacles preventing that, so it got enabled with a warning
+ about potential dangers when the device will not do the duplicated copies. See
+ mkfs.btrfs section DUP PROFILES ON A SINGLE DEVICE.
+
+ * support balance filters added/enhanced in linux 4.4
+ * usage=min..max -- enhanced to take range
+ * stripes=min..max -- new, filter by stripes for raid0/10/5/6
+ * limit=min..max -- enhanced to take range
+ Note: due to backward compatibility, the range maximum for 'usage' is not
+ inclusive as for the others, to keep the same behaviour as usage=N .
+
+ * manual pages enhanced (btrfs, mkfs, mount, filesystem, balance)
+ * error messages updates, rewordings -- some fstests may break due to that
+ * added support for free-space-tree implementation of space cache -- this
+ requires kernel 4.5 and is not recommended for non-developers yet
+ * btrfs filesystem usage works with mixed blockgroups
+
+ Other:
+ * installation to /usr/local -- this has unintentionally changed during
+ conversion to autotools in 3.19
+ * check: fix a false alert where extent record has wrong metadata flag
+ * improved stability on fuzzed/crafted images when reading sys array in
+ superblock
+ * build: the 'ar' tool is properly deteced during cross-compilation
+ * debug-tree: option -t understands ids for tree root and chnuk tree
+ * preparatory work for btrfs-convert rewrite
+ * sparse, gcc warning fixes
+ * more memory allocation failure handling
+ * cleanups
+ * more tests
+
+ Bugfixes:
+ * chunk recovery: fix floating point exception
+ * chunk recovery: endianity bugfix during rebuild
+ * mkfs with 64K pages and nodesize reported superblock checksum mismatch
+ * check: properly reset nlink of multi-linked file
+
+btrfs-progs-4.3.1 (2015-11-16)
+ * fixes
+ * device delete: recognize 'missing' again
+ * mkfs: long names are not trimmed when doing ssd check
+ * support partitioned loop devices
+ * other
+ * replace several mallocs with on-stack variables
+ * more memory allocation failure handling
+ * add tests for bugs fixed
+ * cmd-device: switch to new message printing helpers
+ * minor code cleanups
+
+btrfs-progs-4.3 (2015-11-06)
+ * mkfs
+ * mixed mode is not forced for filesystems smaller than 1GiB
+ * mixed mode broken with mismatching sectorsize and nodesize, fixed
+ * print version info earlier
+ * print devices sorted by id
+ * do not truncate target image with --rootsize
+ * fi usage:
+ * don't print global block reserve
+ * print device id
+ * minor output tuning
+ * other cleanups
+ * calc-size:
+ * div-by-zero fix on an empty filesystem
+ * fix crash
+ * bugfixes:
+ * more superblock sanity checks
+ * consistently round size of all devices down to sectorsize
+ * misc leak fixes
+ * convert: don't try to rollback with a half-deleted ext2_saved
+ subvolume
+ * other:
+ * check: add progress indicator
+ * scrub: enahced error message
+ * show-super: read superblock from a given offset
+ * add README
+ * docs: update manual page for mkfs.btrfs, btrfstune, balance,
+ convert and inspect-internal
+ * build: optional build with more warnings (W=...)
+ * build: better support for static checkers
+ * build: html output of documentation
+ * pretty-print: last_snapshot for root_item
+ * pretty-print: stripe dev uuid
+ * error reporting wrappers, introduced and example use
+ * refactor open_file_or_dir
+ * other docs and help updates
+ * testing:
+ * test for nodes crossing stripes
+ * test for broken 'subvolume sync'
+ * basic tests for mkfs, raid option combinations
+ * basic tests for fuzzed images (check)
+ * command intrumentation (eg valgrind)
+ * print commands if requested
+ * add README for tests
+
+btrfs-progs-4.2.3 (2015-10-19)
+ * subvol sync: make it actually work again: it's been broken since 4.1.2,
+ due to a reversed condition it returned immediatelly instead of waiting
+ * scanning: do not scan already discovered filesystems (minor optimization)
+ * convert: better error message in case the filesystem is not finalized
+ * 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
+ * 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
+
+btrfs-progs-4.2.1 (2015-09-20)
+ * fix an off-by-one error in cross-stripe boundary check
+ * if nodesize was 64k, any metadata block was reported as crossing,
+ this leads to mkfs failure for example due to "no free blocks
+ found"
+ * for other nodesizes, if the end of the metadata block was 64k
+ aligned, it was incorrectly reported by fsck
+ * convert: don't write uninitialized data to image
+ * image:
+ * don't loop with option -t0
+ * don't create threads if compression is not requested
+ * other: minor cleanups
+
+btrfs-progs-4.2 (2015-09-03)
+ * enhancements:
+ * mkfs: do not create extra single chunks on multiple devices
+ * resize: try to guess the minimal size, 'inspect min-dev-size'
+ * qgroup assign: add option to schedule rescan
+ * chunk-recover: be more verbose about the scanning process
+ * fixes:
+ * check:
+ * find stripes crossing stripe boundary -- created by convert
+ * print correct range for file hole when there are no extents
+ and learn how to fix it
+ * replace: more sanity checks
+ * convert: concurrency fixes related to reporting progress
+ * find-root: option -a will not skip the current root anymore
+ * subvol list: fix occasional crash
+ * do not create stripes crossing stripe boundary
+ * build:
+ * fixes for musl libc
+ * preliminary support for android (not working yet, more code changes
+ needed)
+ * new EXTRA_CFLAGS and EXTRA_LDFLAGS
+ * other:
+ * lots of cleanups
+ * tests: lots of updates, new tests, framework improvements
+ * documentation updates
+ * debugging: print-tree shows stripe length
+
+btrfs-progs-4.1.2 (2015-07-14)
+ * urgent bugfix: mkfs creates invalid filesystem, must be recreated
+
+btrfs-progs-4.1.1 (2015-07-10) -- Do not use this version!
+ Bugfixes:
+ * defrag: threshold overflow fix
+ * fsck:
+ * check if items fit into the leaf space
+ * fix wrong nbytes
+ * mkfs:
+ * create only desired block groups for single device
+ * preparatory work for fix on multiple devices
+ Enhancements:
+ * new alias for 'device delete': 'device remove'
+ Other:
+ * fix compilation on old gcc (4.3)
+ * documentation updates
+ * debug-tree: print nbytes
+ * test: image for corrupted nbytes
+ * corupt-block: let it kill nbytes
+
+btrfs-progs-4.1 (2015-06-22)
+ Bugfixes:
+ * fsck.btrfs: no bash-isms
+ * bugzilla 97171: invalid memory access (with tests)
+ * receive:
+ * cloning works with --chroot
+ * capabilities not lost
+ * mkfs: do not try to register bare file images
+ * option --help accepted by the standalone utilities
+
+ Enhancements:
+ * corrupt block: ability to remove csums
+ * mkfs:
+ * warn if metadata redundancy is lower than for data
+ * options to make the output quiet (only errors)
+ * mixed case names of raid profiles accepted
+ * rework the output:
+ * more comprehensive, 'key: value' format
+ * subvol:
+ * show:
+ * print received uuid
+ * update the output
+ * new options to specify size units
+ * sync: grab all deleted ids and print them as they're removed,
+ previous implementation only checked if there are any to be
+ deleted - change in command semantics
+ * scrub: print timestamps in days HMS format
+ * receive:
+ * can specify mount point, do not rely on /proc
+ * can work inside subvolumes
+ * send: new option to send stream without data (NO_FILE_DATA)
+ * convert: specify incompat features on the new fs
+ * qgroup:
+ * show: distinguish no limits and 0 limit value
+ * limit: ability to clear the limit
+ * help for 'btrfs' is shorter, 1st level command overview
+ * debug tree: print key names according to their C name
+
+ New:
+ * rescure zero-log
+ * btrfsune:
+ * rewrite uuid on a filesystem image
+ * new option to turn on NO_HOLES incompat feature
+
+ Deprecated:
+ * standalone btrfs-zero-log
+
+ Other:
+ * testing framework updates
+ * uuid rewrite test
+ * btrfstune feature setting test
+ * zero-log tests
+ * more testing image formats
+ * manual page updates
+ * ioctl.h synced with current kernel uapi version
+ * convert: preparatory works for more filesystems (reiserfs pending)
+ * use static buffers for path handling where possible
+ * add new helpers for send uilts that check memory allocations,
+ switch all users, deprecate old helpers
+ * Makefile: fix build dependency generation
+ * map-logical: make it work again
+
+btrfs-progs-4.0.1 (2015-05-20)
+ * restore:
+ * can restore symlinks, new option --symlinks
+ * long option variants added
+ * convert: dropped dependency on acl.h header and libacl is not required
+ for build
+ * fix for 'check' crash
+ * device remove error message fix
+ * preparatory works for fsid change
+
+btrfs-progs-4.0 (2015-04-29)
+ * resize:
+ * don't accept file as an argument (it's confusing)
+ * print better error message in case of an error
+ * restore: optionally restore metadata (time, mode, uid/gid)
+ * receive: optionally enforce chroot
+ * new rescue subcommand 'zero-log', same as btrfs-zero-log, but now also
+ part of the main utility
+ * check:
+ * free space checks match kernel, fixes incorrect reports
+ * convert: fix setting of checksum bit if --no-datasum is used
+ * fsck.btrfs: don't print messages
+ * fix quota rescan on PPC64 (mangled ioctl number)
+ * test updates
+ * documentation: files renamed to .asciidoc, misc fixups
+
+btrfs-progs-3.19.1 (2015-03-25)
+ * convert:
+ * new option to specify metadata block size
+ * --no-progress actually works
+ * restore: properly handle the page boundary corner case
+ * build fixes:
+ * missing macro from public header, BTRFS_BUILD_VERSION
+ * wrong handling of --enable-convert
+ * fi usage: reports correct space for degraded mounts
+ * other:
+ * mkfs: help string updates
+ * completion: added 'usage' subcommands
+ * cleanups in qgroup code, preparatory work
+
+btrfs-progs-3.19 (2015-03-11)
+ * build converted to autotools
+ * btrfs-image
+ * restore can now run in parallel threads
+ * fixed restore of multiple image from multiple devices onto a single
+ dev
+ * introduced metadump v2
+ * check: make --init-csum-tree and --init-extent-tree work together
+ * find-new: option to search through all metadata even if a root was
+ already found
+ * convert: show progress by default, can be turned off
+ * corrupt-block: option to work on a specific root
+ * bash completion script for all subcommands
+
+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
+ * tests: new infrastructure
+ * btrfstune: force flag can be used together with seeding option
+ * backtrace support is back
+ * getopt cleanups
+ * doc and help updates
+
+btrfs-progs-3.18.1 (2015-01-09)
+ * minor fixes
+ * documentation updates
+
+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'
+ * device usage - more detailed information about per-device allocations
+ * check
+ * option to set a different tree root byte number
+ * ability to link lost files to lost+found, caused by a recent kernel
+ bug
+ * repair of severely corrupted fs (use with care)
+ * convert - option to show progress
+ * subvol create - print the commit mode inline, print the global mode only
+ if --verbose
+ * other updates: musl-libc support, coverity bugfixes, new test images,
+ documentation
+
+btrfs-progs-3.17.3 (2014-12-04)
+ * convert: fix conversion of sparse ext* filesystems
+ * show: resolve to the correct path
+ * fsck: more verbose error for root dir problems
+
+btrfs-progs-3.17.2 (2014-11-19)
+ * check improvements
+ * add ability to replace missing dir item/dir indexes
+ * fix missing inode items
+ * create missing root dirid
+ * corrupt block: enhancements for testing fsck
+ * 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
+ * fix linking with libbtrfs
+ * replace: better error reporting
+ * fi show: fixed stall if run concurrently with balance
+ * check: fixed argument parsing for --subvol-extents
+ * fi df: SI prefixes corrected
+
+btrfs-progs-3.17 (2014-10-17)
+ * check: --init-csum-tree actually does something useful, rebuilds the
+ whole csum tree
+ * /dev scanning for btrfs devices is gone
+ * /proc/partitions scanning is gone, blkid is used exclusively
+ * new subcommand subvolume sync
+ * filesystem df: new options to set unit format
+ * convert: allow to copy label from the origin, or specify a new one
+
+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
+ * new option -R in subvol list
+ * library version defines
+ * static build is fixed
+ * build without documentation is possible
+
+btrfs-progs-3.16 (2014-08-26)
+ * mkfs: new option to specify UUID, drop experimental notice
+ * check: new option to verify quotas, reduced memory requirements, new
+ option to print extent sharing
+ * restore: check length before decompression, more error handling, option
+ to loop during restoring
+ * balance: new filter 'limit'
+ * recover: allow to read all sb copies
+ * btrfstune: new option to force dangerous changes
+ * receive: new option to limit number of errors
+ * show-super: skip unrecognized sb, add option to force
+ * debug-tree: print tree by id
+ * documentation updates
+
+btrfs-progs-3.14.2 (2014-05-29)
+ * documentation is now written in asciidoc and there are manpages for each
+ subcommand
+ * misc bugfixes
+
+btrfs-progs-3.14.1 (2014-04-18)
+ * properties: fix handling of option -t
+ * restore: fix reading of compressed extents
+ * minor code and doc updates
+
+btrfs-progs-3.14 (2014-04-06)
+ * fsck: fixes and enhancements to --init-extent-tree mode
+ * fsck: chunk-recover updates
+ * scrub: add force option -f
+ * send: check if subvolumes are read-only
+ * subvol delete: add options to affect commit behaviour
+ * btrfs: add property command group
+ * restore: add dry-run option
+ * restore: fix restoring of compressed files
+ * mkfs: support for no-holes feature
+ * mkfs: option -r deals with hardlinks and relative paths
+ * mkfs: discard phase is interruptible
+ * documentation updates
+
+btrfs-progs-3.12 (2013-11-25)
+ * announcement, tarballs
+ * first release after 0.19 (2009/06) with a lot of changes
diff --git a/Documentation/Makefile.in b/Documentation/Makefile.in
index f046abd5..aea2cb47 100644
--- a/Documentation/Makefile.in
+++ b/Documentation/Makefile.in
@@ -4,11 +4,9 @@ MAN8_TXT =
# Top level commands
MAN8_TXT += btrfs.asciidoc
MAN8_TXT += btrfs-convert.asciidoc
-MAN8_TXT += btrfs-debug-tree.asciidoc
MAN8_TXT += btrfs-find-root.asciidoc
MAN8_TXT += btrfs-image.asciidoc
MAN8_TXT += btrfs-map-logical.asciidoc
-MAN8_TXT += btrfs-show-super.asciidoc
MAN8_TXT += btrfs-select-super.asciidoc
MAN8_TXT += btrfstune.asciidoc
MAN8_TXT += fsck.btrfs.asciidoc
@@ -96,6 +94,8 @@ install-man: man
$(INSTALL) -m 644 $(GZ_MAN8) $(DESTDIR)$(man8dir)
$(LN_S) -f btrfs-check.8.gz $(DESTDIR)$(man8dir)/btrfsck.8.gz
$(LN_S) -f btrfs-rescue.8.gz $(DESTDIR)$(man8dir)/btrfs-zero-log.8.gz
+ $(LN_S) -f btrfs-inspect-internal.8.gz $(DESTDIR)$(man8dir)/btrfs-debug-tree.8.gz
+ $(LN_S) -f btrfs-inspect-internal.8.gz $(DESTDIR)$(man8dir)/btrfs-show-super.8.gz
uninstall:
cd $(DESTDIR)$(man8dir); rm -f btrfs-check.8.gz $(GZ_MAN8)
diff --git a/Documentation/btrfs-balance.asciidoc b/Documentation/btrfs-balance.asciidoc
index c8407419..7df40b9c 100644
--- a/Documentation/btrfs-balance.asciidoc
+++ b/Documentation/btrfs-balance.asciidoc
@@ -11,7 +11,7 @@ SYNOPSIS
DESCRIPTION
-----------
-The primary purpose of the balance feature is to spread block groups accross
+The primary purpose of the balance feature is to spread block groups across
all devices so they match constraints defined by the respective profiles. See
`mkfs.btrfs`(8) section 'PROFILES' for more details.
The scope of the balancing process can be further tuned by use of filters that
@@ -67,6 +67,12 @@ resume interrupted balance
start the balance operation according to the specified filters, no filters
will rewrite the entire filesystem. The process runs in the foreground.
+
+NOTE: the balance command without filters will basically rewrite everything
+in the filesystem. The run time is potentially very long, depending on the
+filesystem size. To prevent starting a full balance by accident, the user is
+warned and has a few seconds to cancel the operation before it starts. The
+warning and delay can be skipped with '--full-balance' option.
++
`Options`
+
-d[<filters>]::::
@@ -93,7 +99,7 @@ moving data from single to RAID1). This functionality is accessed through the
'-d', '-m' or '-s' options to btrfs balance start, which filter on data,
metadata and system blocks respectively.
-A filter has the following stucture: 'type'[='params'][,'type'=...]
+A filter has the following structure: 'type'[='params'][,'type'=...]
The available types are:
@@ -106,7 +112,7 @@ are a list of profile names separated by "'|'" (pipe).
Balances only block groups with usage under the given percentage. The
value of 0 is allowed and will clean up completely unused block groups, this
should not require any new work space allocated. You may want to use 'usage=0'
-in case balance is returnin ENOSPC and your filesystem is not too full.
+in case balance is returning ENOSPC and your filesystem is not too full.
+
The argument may be a single value or a range. The single value 'N' means 'at
most N percent used', equivalent to '..N' range syntax. Kernels prior to 4.4
@@ -147,7 +153,7 @@ only the single value format. The range minimum and maximum are inclusive.
*stripes=<range>*::
Balance only block groups which have the given number of stripes. The parameter
-is a range specified as 'start..end'. Makes sense fo block group profiles that
+is a range specified as 'start..end'. Makes sense for block group profiles that
utilize striping, ie. RAID0/10/5/6. The range minimum and maximum are
inclusive.
@@ -192,7 +198,7 @@ command:
An example of a filter that does not require workspace is 'usage=0'. This will
scan through all unused block groups of a given type and will reclaim the
-space. Ater that it might be possible to run other filters.
+space. After that it might be possible to run other filters.
**CONVERSIONS ON MULTIPLE DEVICES**
diff --git a/Documentation/btrfs-check.asciidoc b/Documentation/btrfs-check.asciidoc
index 327a45d6..7371a23c 100644
--- a/Documentation/btrfs-check.asciidoc
+++ b/Documentation/btrfs-check.asciidoc
@@ -25,8 +25,12 @@ OPTIONS
-s|--super <superblock>::
use <superblock>th superblock copy, valid values are 0 up to 2 if the
respective superblock offset is within the filesystem
+-b|--backup::
+use the first backup roots stored in the superblock that is valid
--repair::
try to repair the filesystem
+--readonly::
+run in read-only mode (default)
--init-csum-tree::
create a new CRC tree and recalculate all checksums
--init-extent-tree::
@@ -37,10 +41,12 @@ verify checksums of data blocks
indicate progress at various checking phases
--qgroup-report::
verify qgroup accounting and compare against filesystem accounting
---subvol-extents <subvolid>::
-show extent state for a subvolume
---tree-root <bytenr>::
+-E|--subvol-extents <subvolid>::
+show extent state for the given subvolume
+-r|--tree-root <bytenr>::
use the given bytenr for the tree root
+--chunk-root <bytenr>::
+use the given bytenr for the chunk tree root
EXIT STATUS
-----------
diff --git a/Documentation/btrfs-convert.asciidoc b/Documentation/btrfs-convert.asciidoc
index ca3417f4..28f9a394 100644
--- a/Documentation/btrfs-convert.asciidoc
+++ b/Documentation/btrfs-convert.asciidoc
@@ -75,7 +75,7 @@ consumption and may help to convert a filesystem with low free space
-N|--nodesize <SIZE>::
set filesystem nodesize, the tree block size in which btrfs stores its metadata.
The default value is 16KB (16384) or the page size, whichever is bigger.
-Must be a multiple of the sectorsize, but not larger than 65536. Se
+Must be a multiple of the sectorsize, but not larger than 65536. See
`mkfs.btrfs`(8) for more details.
-r|--rollback::
rollback to the original ext2/3/4 filesystem if possible
@@ -83,6 +83,13 @@ rollback to the original ext2/3/4 filesystem if possible
set filesystem label during conversion
-L|--copy-label::
use label from the converted filesystem
+-O|--features <feature1>[,<feature2>...]::
+A list of filesystem features turned on at btrfs-convert time. Not all features
+are supported by old kernels. To disable a feature, prefix it with '^'.
++
+To see all available features that btrfs-convert supports run:
++
++btrfs-convert -O list-all+
-p|--progress::
show progress of conversion, on by default
--no-progress::
diff --git a/Documentation/btrfs-debug-tree.asciidoc b/Documentation/btrfs-debug-tree.asciidoc
deleted file mode 100644
index 23fc1156..00000000
--- a/Documentation/btrfs-debug-tree.asciidoc
+++ /dev/null
@@ -1,38 +0,0 @@
-btrfs-debug-tree(8)
-===================
-
-NAME
-----
-btrfs-debug-tree - dump btrfs filesystem metadata into stdout
-
-SYNOPSIS
---------
-*btrfs-debug-tree* [options] <device>
-
-DESCRIPTION
------------
-*btrfs-debug-tree* is used to dump the whole tree of the given device.
-
-This is maybe useful for analyzing filesystem state or inconsistence and has
-a positive educational effect on understanding the internal structure.
-<device> is the device file where the filesystem is stored.
-
-OPTIONS
--------
--e::
-Print detailed extents info.
--d::
-Print info of btrfs device and root tree dirs only.
--r::
-Print info of roots only.
--b <block_num>::
-Print info of the specified block only.
-
-EXIT STATUS
------------
-*btrfs-debug-tree* will return 0 if no error happened.
-If any problems happened, 1 will be returned.
-
-SEE ALSO
---------
-`mkfs.btrfs`(8)
diff --git a/Documentation/btrfs-device.asciidoc b/Documentation/btrfs-device.asciidoc
index 2827598a..edd9b98e 100644
--- a/Documentation/btrfs-device.asciidoc
+++ b/Documentation/btrfs-device.asciidoc
@@ -74,11 +74,11 @@ do not perform discard by default
-f|--force::::
force overwrite of existing filesystem on the given disk(s)
-*remove* <dev> [<dev>...] <path>::
+*remove* <dev>|<devid> [<dev>|<devid>...] <path>::
Remove device(s) from a filesystem identified by <path>.
-*delete* <dev> [<dev>...] <path>::
-Alias of remove kept for backwards compatability
+*delete* <dev>|<devid> [<dev>|<devid>...] <path>::
+Alias of remove kept for backward compatibility
*ready* <device>::
Check device to see if it has all of it's devices in cache for mounting.
@@ -89,8 +89,8 @@ Scan devices for a btrfs filesystem.
If one or more devices are passed, these are scanned for a btrfs filesystem.
If no devices are passed, btrfs uses block devices containing btrfs
filesystem as listed by blkid.
-Finally, if '--all-devices' or '-d' is passed, all the devices under /dev are
-scanned.
+Finally, '--all-devices' or '-d' is the deprecated option. If it is passed,
+its behavior is the same as if no devices are passed.
*stats* [-z] <path>|<device>::
Read and print the device IO stats for all mounted devices of the filesystem
diff --git a/Documentation/btrfs-filesystem.asciidoc b/Documentation/btrfs-filesystem.asciidoc
index 26126175..dc032faa 100644
--- a/Documentation/btrfs-filesystem.asciidoc
+++ b/Documentation/btrfs-filesystem.asciidoc
@@ -39,7 +39,7 @@ GlobalReserve, single: total=512.00MiB, used=0.00B
------------------------------
+
--
-* 'Data', 'System' and 'Metadata' are separeate block group types.
+* 'Data', 'System' and 'Metadata' are separate block group types.
'GlobalReserve' is an artificial and internal emergency space, see below.
* 'single' -- the allocation profile, defined at mkfs time
* 'total' -- sum of space reserved for
@@ -79,11 +79,11 @@ show sizes in TiB, or TB with --si
If conflicting options are passed, the last one takes precedence.
*defragment* [options] <file>|<dir> [<file>|<dir>...]::
-Defragment file data on a mounted filesytem.
+Defragment file data on a mounted filesystem.
+
If '-r' is passed, files in dir will be defragmented recursively.
The start position and the number of bytes to defragment can be specified by
-start and len using '-s' and '-l' options below.
+start and length using '-s' and '-l' options below.
Extents bigger than value given by '-t' will be skipped, otherwise this value
is used as a target extent size, but is only advisory and may not be reached
if the free space is too fragmented.
@@ -97,6 +97,14 @@ snapshots or de-duplicated data).
This may cause considerable increase of space usage depending on the broken up
ref-links.
+
+NOTE: Directory arguments without '-r' do not defragment files recursively but will
+defragment certain internal trees (extent tree and the subvolume tree). This has been
+confusing and could be removed in the future.
++
+For 'start', 'len', 'size' it is possible to append
+units designator: \'K', \'M', \'G', \'T', \'P', or \'E', which represent
+KiB, MiB, GiB, TiB, PiB, or EiB, respectively (case does not matter).
++
`Options`
+
-v::::
@@ -117,14 +125,41 @@ defragmentation will start from the given offset, default is beginning of a file
defragment only up to 'len' bytes, default is the file size
-t <size>[kKmMgGtTpPeE]::::
target extent size, do not touch extents bigger than 'size'
+
+*du* [options] <path> [<path>..]::
+Calculate disk usage of the target files using FIEMAP. For individual
+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.
+
-For 'start', 'len', 'size' it is possible to append
-units designator: \'K', \'M', \'G', \'T', \'P', or \'E', which represent
-KiB, MiB, GiB, TiB, PiB, or EiB, respectively (case does not matter).
+Each argument to 'btrfs fi 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.
+
-NOTE: Directory arguments without '-r' do not defragment files recursively but will
-defragment certain internal trees (extent tree and the subvolume tree). This has been
-confusing and could be removed in the future.
+'set shared' takes into account overlapping shared extents, hence it
+isn't as simple as adding up shared extents.
++
+`Options`
++
+-s|--summarize::::
+display only a total for each argument
+--raw::::
+raw numbers in bytes, without the 'B' suffix.
+--human-readable::::
+print human friendly numbers, base 1024, this is the default
+--iec::::
+select the 1024 base for the following options, according to the IEC standard.
+--si::::
+select the 1000 base for the following options, according to the SI standard.
+--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.
*label* [<dev>|<mountpoint>] [<newlabel>]::
Show or update the label of a filesystem. This works on a mounted filesystem or
@@ -177,7 +212,7 @@ Show the btrfs filesystem with some additional info about devices and space
allocation.
+
If no option none of 'path'/'uuid'/'device'/'label' is passed, information
-about all the BTRFS filesystems is shown, both mounted and unmounted.
+about all the BTRFS filesystems is shown, both mounted and unmounted.
+
`Options`
+
@@ -286,18 +321,18 @@ of IO load and the system may stall for a moment.
*$ btrfs filesystem defrag -v -r -f dir/*
-Recusively defragment files under 'dir/', be verbose and wait until all blocks
+Recursively defragment files under 'dir/', be verbose and wait until all blocks
are flushed before processing next file. You can note slower progress of the
output and lower IO load (proportional to currently defragmented file).
*$ btrfs filesystem defrag -v -r -f -clzo dir/*
-Recusively defragment files under 'dir/', be verbose, wait until all blocks are
+Recursively defragment files under 'dir/', be verbose, wait until all blocks are
flushed and force file compression.
*$ btrfs filesystem defrag -v -r -t 64M dir/*
-Recusively defragment files under 'dir/', be verbose and try to merge extents
+Recursively defragment files under 'dir/', be verbose and try to merge extents
to be about 64MiB. As stated above, the success rate depends on actual free
space fragmentation and the final result is not guaranteed to meet the target
even if run repeatedly.
diff --git a/Documentation/btrfs-inspect-internal.asciidoc b/Documentation/btrfs-inspect-internal.asciidoc
index 1c7c3611..74f6dea8 100644
--- a/Documentation/btrfs-inspect-internal.asciidoc
+++ b/Documentation/btrfs-inspect-internal.asciidoc
@@ -19,6 +19,75 @@ requires calls to privileged ioctls.
SUBCOMMAND
----------
+*dump-super* [options] <device> [device...]::
+(replaces the standalone tool *btrfs-show-super*)
++
+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
+checks. The superblock checksum status is reported, the device item and
+filesystem UUIDs are checked and reported.
++
+`Options`
++
+-f|--full::::
+print full superblock information, including the system chunk array and backup roots
+-a|--all::::
+print information about all present superblock copies (cannot be used together with '-i' option)
+-i <super_mirror>::::
+specify which mirror to print, valid values are 0, 1 and 2 and the superblock must be present on the device
++
+If there are multiple options specified, only the last one is 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.
++
+-s <bytenr>::::
+specify offset to a superblock in a non-standard location at 'bytenr', useful
+for debugging (disables the '-f' option)
+
+*dump-tree* [options] <device>::
+(replaces the standalone tool *btrfs-debug-tree*)
++
+Dump tree structures from a given device in textual form, expand keys to human
+readable equivalents where possible.
+This is useful for analyzing filesystem state or inconsistencies and has
+a positive educational effect on understanding the internal filesystem structure.
++
+NOTE: contains file names, consider that if you're asked to send the dump for
+analysis. Does not contain file data.
++
+`Options`
++
+-e|--extents::::
+print only extent-related information: extent and device trees
+-d|--device::::
+print only device-related information: tree root, chunk and device trees
+-r|--roots::::
+print only short root node information, ie. the root tree keys
+-R|--backups::::
+same as --roots plus print backup root info, ie. the backup root keys and
+the respective tree root block offset
+-u|--uuid::::
+print only the uuid tree information, empty output if the tree does not exist
+-b <block_num>::::
+print info of the specified block only
+-t <tree_id>::::
+print only the tree with the specified ID, where the ID can be numerical or
+common name in a flexible human readable form
++
+The tree id name recognition rules:
+[options="compact"]
+* case does not matter
+* the C source definition, eg. BTRFS_ROOT_TREE_OBJECTID
+* short forms without BTRFS_ prefix, without _TREE and _OBJECTID suffix, eg. ROOT_TREE, ROOT
+* convenience aliases, eg. DEVICE for the DEV tree, CHECKSUM for CSUM
+* unrecognized ID is an error
+
*inode-resolve* [-v] <ino> <path>::
(needs root privileges)
+
@@ -67,6 +136,16 @@ inode number 2), but such subvolume does not contain any files anyway
+
resolve the absolute path of a the subvolume id 'subvolid'
+*tree-stats* [options] <device>::
+(needs root privileges)
++
+Print sizes and statistics of trees.
++
+`Options`
++
+-b::::
+Print raw numbers in bytes.
+
EXIT STATUS
-----------
*btrfs inspect-internal* returns a zero exit status if it succeeds. Non zero is
@@ -80,5 +159,4 @@ further details.
SEE ALSO
--------
-`mkfs.btrfs`(8),
-`btrfs-debug-tree`(8)
+`mkfs.btrfs`(8)
diff --git a/Documentation/btrfs-man5.asciidoc b/Documentation/btrfs-man5.asciidoc
index d4323917..e2eea263 100644
--- a/Documentation/btrfs-man5.asciidoc
+++ b/Documentation/btrfs-man5.asciidoc
@@ -35,7 +35,7 @@ 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 not practial use, it's slated to be
+This option was used for testing and has no practical use, it's slated to be
removed in the future.
*autodefrag*::
@@ -72,7 +72,7 @@ The write flushes incur a slight hit and also prevent the IO block
scheduler to reorder requests in more effective way. Disabling barriers gets
rid of that penalty but will most certainly lead to a corrupted filesystem in
case of a crash or power loss. The ordinary metadata blocks could be yet
-unwrittent at the time the new superblock is stored permanently, expecting that
+unwritten at the time the new superblock is stored permanently, expecting that
the block pointers to metadata were stored permanently before.
+
On a device with a volatile battery-backed write-back cache, the 'nobarrier'
@@ -167,7 +167,7 @@ system at that point.
Enable discarding of freed file blocks using TRIM operation. This is useful
for SSD devices, thinly provisioned LUNs or virtual machine images where the
backing device understands the operation. Depending on support of the
-underlying device, the operation may severly hurt performance in case the TRIM
+underlying device, the operation may severely hurt performance in case the TRIM
operation is synchronous (eg. with SATA devices up to revision 3.0).
+
If discarding is not necessary to be done at the block freeing time, there's
@@ -278,13 +278,25 @@ May be resumed with *btrfs balance resume* or the paused state can be removed
by *btrfs balance cancel*.
*space_cache*::
+*space_cache=v2*::
*nospace_cache*::
-('nospace_cache' since: 3.2, default: on)
+('nospace_cache' since: 3.2, 'space_cache=v2' since 4.5, default: on)
+
-Disable freespace cache loading without clearing the cache and the free space
-cache will not be used during the mount. This affects performance as searching
-for new free blocks could take longer. On the other hand, managing the space
-cache consumes some resources.
+Options to control the free space cache. This affects performance as searching
+for new free blocks could take longer if the space cache is not enabled. On the
+other hand, managing the space cache consumes some resources. It can be
+disabled without clearing at mount time.
++
+There are two implementations of how the space is tracked. The safe default is
+'v1'. On large filesystems (many-terabytes) and certain workloads the 'v1'
+performance may degrade. This problem is addressed by 'v2', that is based on
+b-trees, sometimes referred to as 'free-space-tree'.
++
+'Compatibility notes:'
++
+* the 'v2' has to be enabled manually at mount time, once
+* kernel without 'v2' support will be able to mount the filesystem in read-only mode
+* 'v2' can be removed by mounting with 'clear_cache'
*ssd*::
*nossd*::
@@ -293,7 +305,7 @@ cache consumes some resources.
+
Options to control SSD allocation schemes. By default, BTRFS will
enable or disable SSD allocation heuristics depending on whether a
-rotational or nonrotational disk is in use. The 'ssd' and 'nossd' options
+rotational or non-rotational disk is in use. The 'ssd' and 'nossd' options
can override this autodetection.
+
The 'ssd_spread' mount option attempts to allocate into bigger and aligned
@@ -337,7 +349,7 @@ such syncs, the pending tree log operations are replayed during mount.
WARNING: currently, the tree log is replayed even with a read-only mount!
+
The tree log could contain new files/directories, these would not exist on
-a mounted filesystm if the log is not replayed.
+a mounted filesystem if the log is not replayed.
*user_subvol_rm_allowed*::
(default: off)
diff --git a/Documentation/btrfs-qgroup.asciidoc b/Documentation/btrfs-qgroup.asciidoc
index 57cf012d..438dbc7d 100644
--- a/Documentation/btrfs-qgroup.asciidoc
+++ b/Documentation/btrfs-qgroup.asciidoc
@@ -25,7 +25,7 @@ Quota groups or qgroup in btrfs make a tree hierarchy, the leaf qgroups are
attached to subvolumes. The size limits are set per qgroup and apply when any
limit is reached in tree that contains a given subvolume.
-The limit sare separated between shared and exclusive and reflect the extent
+The limits are separated between shared and exclusive and reflect the extent
ownership. For example a fresh snapshot shares almost all the blocks with the
original subvolume, new writes to either subvolume will raise towards the
exclusive limit.
diff --git a/Documentation/btrfs-receive.asciidoc b/Documentation/btrfs-receive.asciidoc
index 84b85c1c..758eebe3 100644
--- a/Documentation/btrfs-receive.asciidoc
+++ b/Documentation/btrfs-receive.asciidoc
@@ -43,7 +43,7 @@ or on EOF.
--max-errors <N>::
Terminate as soon as N errors happened while processing commands from the send
stream. Default value is 1. A value of 0 means no limit.
--m::
+-m <mountpoint>::
The root mount point of the destination fs.
+
By default the mountpoint is searched in /proc/self/mounts.
diff --git a/Documentation/btrfs-replace.asciidoc b/Documentation/btrfs-replace.asciidoc
index 5a14a40a..a259f96d 100644
--- a/Documentation/btrfs-replace.asciidoc
+++ b/Documentation/btrfs-replace.asciidoc
@@ -33,6 +33,10 @@ 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>.
+
+NOTE: the filesystem has to be resized to fully take advantage of a
+larger target device, this can be achieved with
+`btrfs filesystem resize <devid>:max /path`
++
`Options`
+
-r::::
@@ -57,7 +61,7 @@ Print status and progress information of a running device replace operation.
+
-1::::
print once instead of print continuously until the replace
-operation finishes (or is canceled)
+operation finishes (or is cancelled)
EXIT STATUS
-----------
diff --git a/Documentation/btrfs-scrub.asciidoc b/Documentation/btrfs-scrub.asciidoc
index 7750868d..2335aba2 100644
--- a/Documentation/btrfs-scrub.asciidoc
+++ b/Documentation/btrfs-scrub.asciidoc
@@ -25,7 +25,7 @@ If a <device> is given, the corresponding filesystem is found and
scrub cancel behaves as if it was called on that filesystem.
*resume* [-BdqrR] [-c <ioprio_class> -n <ioprio_classdata>] <path>|<device>::
-Resume a canceled or interrupted scrub cycle on the filesystem identified by
+Resume a cancelled or interrupted scrub cycle on the filesystem identified by
<path> or on a given <device>.
+
Does not start a new scrub if the last scrub finished successfully.
@@ -64,13 +64,13 @@ Set IO priority class (see `ionice`(1) manpage).
Set IO priority classdata (see `ionice`(1) manpage).
-f::::
Force starting new scrub even if a scrub is already running.
-This is useful when scrub stat record file is damaged.
+This is useful when scrub status record file is damaged.
*status* [-d] <path>|<device>::
Show status of a running scrub for the filesystem identified by <path> or
for the specified <device>.
+
-If no scrub is running, show statistics of the last finished or canceled scrub
+If no scrub is running, show statistics of the last finished or cancelled scrub
for that filesystem or device.
+
`Options`
diff --git a/Documentation/btrfs-show-super.asciidoc b/Documentation/btrfs-show-super.asciidoc
deleted file mode 100644
index 8866c940..00000000
--- a/Documentation/btrfs-show-super.asciidoc
+++ /dev/null
@@ -1,54 +0,0 @@
-btrfs-show-super(8)
-====================
-
-NAME
-----
-btrfs-show-super - show btrfs superblock information stored in devices
-
-SYNOPSIS
---------
-*btrfs-show-super* [options] <dev> [<dev>...]
-
-DESCRIPTION
------------
-*btrfs-show-super* is used to print the information of superblock,
-you can specify which mirror to print out.
-
-By default, every device's first superblock will be printed out.
-
-Mainly used for debug purpose.
-
-OPTIONS
--------
--f::
-Print full superblock information.
-+
-Including the system chunk array and backup roots.
-
--a::
-Print information of all superblocks.
-+
-If this option is given, '-i' option will be ignored.
-
--i <super_mirror>::
-Specify which mirror to print out.
-+
-<super_mirror> is between 0 and 2.
-If several '-i <super_mirror>' are given, only the last one is valid.
-
--F::
-Attempt to print the superblock even if no superblock magic is found. May end
-badly.
-
--s <bytenr>::
-specifiy offset to a superblock in a non-standard location at 'bytenr', useful
-for debugging (disables the '-f' option)
-
-EXIT STATUS
------------
-*btrfs-show-super* will return 0 if no error happened.
-If any problems happened, 1 will be returned.
-
-SEE ALSO
---------
-`mkfs.btrfs`(8)
diff --git a/Documentation/btrfs-subvolume.asciidoc b/Documentation/btrfs-subvolume.asciidoc
index 96cfe4ac..5497c57a 100644
--- a/Documentation/btrfs-subvolume.asciidoc
+++ b/Documentation/btrfs-subvolume.asciidoc
@@ -73,7 +73,7 @@ stored on the media.
wait for transaction commit at the end of the operation
+
-C|--commit-each::::
-wait for transaction commit after delet each subvolume
+wait for transaction commit after deleting each subvolume
*find-new* <subvolume> <last_gen>::
List the recently modified files in a subvolume, after <last_gen> ID.
diff --git a/Documentation/btrfs.asciidoc b/Documentation/btrfs.asciidoc
index abf1ff89..6a77a852 100644
--- a/Documentation/btrfs.asciidoc
+++ b/Documentation/btrfs.asciidoc
@@ -15,7 +15,7 @@ The *btrfs* utility is a toolbox for managing btrfs filesystems. There are
command groups to work with subvolumes, devices, for whole filesystem or other
specific actions. See section *COMMANDS*.
-COMMAND SYTNAX
+COMMAND SYNTAX
--------------
Any command name can be shortened as far as it stays unambiguous,
diff --git a/Documentation/btrfstune.asciidoc b/Documentation/btrfstune.asciidoc
index f5cf15e7..68fec4c9 100644
--- a/Documentation/btrfstune.asciidoc
+++ b/Documentation/btrfstune.asciidoc
@@ -57,7 +57,7 @@ If a previously-seeding device is changed, all filesystems that used that
device will become unmountable. Setting the seeding flag back will not fix
that. +
A valid usecase is 'seeding device as a base image'. Clear the seeding
-flag, update the filesystem and make it seeding again, provided that it's ok
+flag, update the filesystem and make it seeding again, provided that it's OK
to throw away all filesystems built on top of the previous base.
EXIT STATUS
diff --git a/Documentation/mkfs.btrfs.asciidoc b/Documentation/mkfs.btrfs.asciidoc
index 6a492658..e4321de9 100644
--- a/Documentation/mkfs.btrfs.asciidoc
+++ b/Documentation/mkfs.btrfs.asciidoc
@@ -230,7 +230,7 @@ Other terms commonly used:
*block group*::
*chunk*::
a logical range of space of a given profile, stores data, metadata or both;
-sometimes the terms are used interchangably
+sometimes the terms are used interchangeably
+
A typical size of metadata block group is 256MiB (filesystem smaller than
50GiB) and 1GiB (larger than 50GiB), for data it's 1GiB. The system block group
@@ -254,7 +254,7 @@ There are the following block group types available:
.2+^.<h| Profile 3+^.^h| Redundancy .2+^.<h| Min/max devices
^.^h| Copies ^.^h| Parity ^.<h| Striping
| single | 1 | | | 1/any
-| DUP | 2 / 1 device | | | 1/1 ^(see note)^
+| DUP | 2 / 1 device | | | 1/any ^(see note)^
| RAID0 | | | 1 to N | 2/any
| RAID1 | 2 | | | 2/any
| RAID10 | 2 | | 1 to N | 4/any
@@ -263,8 +263,8 @@ There are the following block group types available:
|=============================================================
'Note:' DUP may exist on more than 1 device if it starts on a single device and
-another one is added, but *mkfs.btrfs* will not let you create DUP on multiple
-devices.
+another one is added. Since version 4.5.1, *mkfs.btrfs* will let you create DUP
+on multiple devices.
DUP PROFILES ON A SINGLE DEVICE
-------------------------------
@@ -274,7 +274,7 @@ the logical blocks to 2 physical locations. Whether there are really 2
physical copies highly depends on the underlying device type.
For example, a SSD drive can remap the blocks internally to a single copy thus
-deduplicating them. This negates the purpose of increased redunancy and just
+deduplicating them. This negates the purpose of increased redundancy and just
wastes space.
The duplicated data/metadata may still be useful to statistically improve the
diff --git a/Makefile.in b/Makefile.in
index 91847896..0a1aece7 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -75,7 +75,8 @@ 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-property.o cmds-fi-usage.o cmds-inspect-dump-tree.o \
+ cmds-inspect-dump-super.o cmds-inspect-tree-stats.o cmds-fi-du.o
libbtrfs_objects = send-stream.o send-utils.o rbtree.o btrfs-list.o crc32c.o \
uuid-tree.o utils-lib.o rbtree-utils.o
libbtrfs_headers = send-stream.h send-utils.h send.h rbtree.h btrfs-list.h \
@@ -125,6 +126,12 @@ endif
# specify btrfs_foo_libs = <list of libs>; see $($(subst...)) rules below
btrfs_convert_libs = @EXT2FS_LIBS@ @COM_ERR_LIBS@
btrfs_fragments_libs = -lgd -lpng -ljpeg -lfreetype
+btrfs_debug_tree_objects = cmds-inspect-dump-tree.o
+btrfs_show_super_objects = cmds-inspect-dump-super.o
+btrfs_calc_size_objects = cmds-inspect-tree-stats.o
+
+# collect values of the variables above
+standalone_deps = $(foreach dep,$(patsubst %,%_objects,$(subst -,_,$(filter btrfs-%, $(progs)))),$($(dep)))
SUBDIRS =
BUILDDIRS = $(patsubst %,build-%,$(SUBDIRS))
@@ -209,6 +216,10 @@ test-fuzz: btrfs
@echo " [TEST] fuzz-tests.sh"
$(Q)bash tests/fuzz-tests.sh
+test-cli: btrfs
+ @echo " [TEST] cli-tests.sh"
+ $(Q)bash tests/cli-tests.sh
+
test-clean:
@echo "Cleaning tests"
$(Q)bash tests/clean-tests.sh
@@ -248,15 +259,18 @@ $(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)
+btrfs-%.static: $(static_objects) btrfs-%.static.o $(static_libbtrfs_objects) $(patsubst %.o,%.static.o,$(standalone_deps))
@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
+btrfs-%: $(objects) $(libs_static) btrfs-%.o $(standalone_deps)
@echo " [LD] $@"
- $(Q)$(CC) $(CFLAGS) -o $@ $(objects) $@.o $(libs_static) \
+ $(Q)$(CC) $(CFLAGS) -o $@ $(objects) $@.o \
+ $($(subst -,_,$@-objects)) \
+ $(libs_static) \
$(LDFLAGS) $(LIBS) $($(subst -,_,$@-libs))
btrfs: $(objects) btrfs.o $(cmds_objects) $(libs_static)
diff --git a/backref.c b/backref.c
index 8f41f829..9d48a108 100644
--- a/backref.c
+++ b/backref.c
@@ -22,6 +22,7 @@
#include "backref.h"
#include "ulist.h"
#include "transaction.h"
+#include "internal.h"
#define pr_debug(...) do { } while (0)
@@ -450,7 +451,7 @@ static int __add_missing_keys(struct btrfs_fs_info *fs_info,
continue;
BUG_ON(!ref->wanted_disk_byte);
eb = read_tree_block(fs_info->tree_root, ref->wanted_disk_byte,
- fs_info->tree_root->leafsize, 0);
+ fs_info->tree_root->nodesize, 0);
if (!extent_buffer_uptodate(eb)) {
free_extent_buffer(eb);
return -EIO;
@@ -804,8 +805,7 @@ static int find_parent_nodes(struct btrfs_trans_handle *trans,
ref->level == 0) {
u32 bsz;
struct extent_buffer *eb;
- bsz = btrfs_level_size(fs_info->extent_root,
- ref->level);
+ bsz = fs_info->extent_root->nodesize;
eb = read_tree_block(fs_info->extent_root,
ref->parent, bsz, 0);
if (!extent_buffer_uptodate(eb)) {
@@ -1156,7 +1156,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->leafsize;
+ size = fs_info->extent_root->nodesize;
else if (found_key->type == BTRFS_EXTENT_ITEM_KEY)
size = found_key->offset;
diff --git a/btrfs-calc-size.c b/btrfs-calc-size.c
index e3f02d87..835fcc50 100644
--- a/btrfs-calc-size.c
+++ b/btrfs-calc-size.c
@@ -16,491 +16,21 @@
* Boston, MA 021110-1307, USA.
*/
-#include <ctype.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <zlib.h>
-#include "kerncompat.h"
-#include "ctree.h"
-#include "disk-io.h"
-#include "print-tree.h"
-#include "transaction.h"
-#include "list.h"
#include "volumes.h"
#include "utils.h"
-
-static int verbose = 0;
-static int no_pretty = 0;
-
-struct seek {
- u64 distance;
- u64 count;
- struct rb_node n;
-};
-
-struct root_stats {
- u64 total_nodes;
- u64 total_leaves;
- u64 total_bytes;
- u64 total_inline;
- u64 total_seeks;
- u64 forward_seeks;
- u64 backward_seeks;
- u64 total_seek_len;
- u64 max_seek_len;
- u64 total_clusters;
- u64 total_cluster_size;
- u64 min_cluster_size;
- u64 max_cluster_size;
- u64 lowest_bytenr;
- u64 highest_bytenr;
- struct rb_root seek_root;
- int total_levels;
-};
-
-static int add_seek(struct rb_root *root, u64 dist)
-{
- struct rb_node **p = &root->rb_node;
- struct rb_node *parent = NULL;
- struct seek *seek = NULL;
-
- while (*p) {
- parent = *p;
- seek = rb_entry(parent, struct seek, n);
-
- if (dist < seek->distance) {
- p = &(*p)->rb_left;
- } else if (dist > seek->distance) {
- p = &(*p)->rb_right;
- } else {
- seek->count++;
- return 0;
- }
- }
-
- seek = malloc(sizeof(struct seek));
- if (!seek)
- return -ENOMEM;
- seek->distance = dist;
- seek->count = 1;
- rb_link_node(&seek->n, parent, p);
- rb_insert_color(&seek->n, root);
- return 0;
-}
-
-static int walk_leaf(struct btrfs_root *root, struct btrfs_path *path,
- struct root_stats *stat, int find_inline)
-{
- struct extent_buffer *b = path->nodes[0];
- struct btrfs_file_extent_item *fi;
- struct btrfs_key found_key;
- int i;
-
- stat->total_bytes += root->leafsize;
- stat->total_leaves++;
-
- if (!find_inline)
- return 0;
-
- for (i = 0; i < btrfs_header_nritems(b); i++) {
- btrfs_item_key_to_cpu(b, &found_key, i);
- if (found_key.type != BTRFS_EXTENT_DATA_KEY)
- continue;
-
- fi = btrfs_item_ptr(b, i, struct btrfs_file_extent_item);
- if (btrfs_file_extent_type(b, fi) == BTRFS_FILE_EXTENT_INLINE)
- stat->total_inline +=
- btrfs_file_extent_inline_item_len(b,
- btrfs_item_nr(i));
- }
-
- return 0;
-}
-
-static u64 calc_distance(u64 block1, u64 block2)
-{
- if (block1 < block2)
- return block2 - block1;
- return block1 - block2;
-}
-
-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];
- u64 last_block;
- u64 cluster_size = root->leafsize;
- int i;
- int ret = 0;
-
- stat->total_bytes += root->nodesize;
- stat->total_nodes++;
-
- last_block = btrfs_header_bytenr(b);
- for (i = 0; i < btrfs_header_nritems(b); i++) {
- struct extent_buffer *tmp = NULL;
- u64 cur_blocknr = btrfs_node_blockptr(b, i);
-
- path->slots[level] = i;
- if ((level - 1) > 0 || find_inline) {
- tmp = read_tree_block(root, cur_blocknr,
- btrfs_level_size(root, level - 1),
- btrfs_node_ptr_generation(b, i));
- if (!extent_buffer_uptodate(tmp)) {
- fprintf(stderr, "Failed to read blocknr %Lu\n",
- btrfs_node_blockptr(b, i));
- continue;
- }
- path->nodes[level - 1] = tmp;
- }
- if (level - 1)
- ret = walk_nodes(root, path, stat, level - 1,
- find_inline);
- else
- ret = walk_leaf(root, path, stat, find_inline);
- if (last_block + root->leafsize != cur_blocknr) {
- u64 distance = calc_distance(last_block +
- root->leafsize,
- cur_blocknr);
- stat->total_seeks++;
- stat->total_seek_len += distance;
- if (stat->max_seek_len < distance)
- stat->max_seek_len = distance;
- if (add_seek(&stat->seek_root, distance)) {
- fprintf(stderr, "Error adding new seek\n");
- ret = -ENOMEM;
- break;
- }
-
- if (last_block < cur_blocknr)
- stat->forward_seeks++;
- else
- stat->backward_seeks++;
- if (cluster_size != root->leafsize) {
- stat->total_cluster_size += cluster_size;
- stat->total_clusters++;
- if (cluster_size < stat->min_cluster_size)
- stat->min_cluster_size = cluster_size;
- if (cluster_size > stat->max_cluster_size)
- stat->max_cluster_size = cluster_size;
- }
- cluster_size = root->leafsize;
- } else {
- cluster_size += root->leafsize;
- }
- last_block = cur_blocknr;
- if (cur_blocknr < stat->lowest_bytenr)
- stat->lowest_bytenr = cur_blocknr;
- if (cur_blocknr > stat->highest_bytenr)
- stat->highest_bytenr = cur_blocknr;
- free_extent_buffer(tmp);
- if (ret) {
- fprintf(stderr, "Error walking down path\n");
- break;
- }
- }
-
- return ret;
-}
-
-static void print_seek_histogram(struct root_stats *stat)
-{
- struct rb_node *n = rb_first(&stat->seek_root);
- struct seek *seek;
- u64 tick_interval;
- u64 group_start = 0;
- u64 group_count = 0;
- u64 group_end = 0;
- u64 i;
- u64 max_seek = stat->max_seek_len;
- int digits = 1;
-
- if (stat->total_seeks < 20)
- return;
-
- while ((max_seek /= 10))
- digits++;
-
- /* Make a tick count as 5% of the total seeks */
- tick_interval = stat->total_seeks / 20;
- printf("\tSeek histogram\n");
- for (; n; n = rb_next(n)) {
- u64 ticks, gticks = 0;
-
- seek = rb_entry(n, struct seek, n);
- ticks = seek->count / tick_interval;
- if (group_count)
- gticks = group_count / tick_interval;
-
- if (ticks <= 2 && gticks <= 2) {
- if (group_count == 0)
- group_start = seek->distance;
- group_end = seek->distance;
- group_count += seek->count;
- continue;
- }
-
- if (group_count) {
-
- gticks = group_count / tick_interval;
- printf("\t\t%*Lu - %*Lu: %*Lu ", digits, group_start,
- digits, group_end, digits, group_count);
- if (gticks) {
- for (i = 0; i < gticks; i++)
- printf("#");
- printf("\n");
- } else {
- printf("|\n");
- }
- group_count = 0;
- }
-
- if (ticks <= 2)
- continue;
-
- printf("\t\t%*Lu - %*Lu: %*Lu ", digits, seek->distance,
- digits, seek->distance, digits, seek->count);
- for (i = 0; i < ticks; i++)
- printf("#");
- printf("\n");
- }
- if (group_count) {
- u64 gticks;
-
- gticks = group_count / tick_interval;
- printf("\t\t%*Lu - %*Lu: %*Lu ", digits, group_start,
- digits, group_end, digits, group_count);
- if (gticks) {
- for (i = 0; i < gticks; i++)
- printf("#");
- printf("\n");
- } else {
- printf("|\n");
- }
- group_count = 0;
- }
-}
-
-static void timeval_subtract(struct timeval *result,struct timeval *x,
- struct timeval *y)
-{
- if (x->tv_usec < y->tv_usec) {
- int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1;
- y->tv_usec -= 1000000 * nsec;
- y->tv_sec += nsec;
- }
-
- if (x->tv_usec - y->tv_usec > 1000000) {
- int nsec = (x->tv_usec - y->tv_usec) / 1000000;
- y->tv_usec += 1000000 * nsec;
- y->tv_sec -= nsec;
- }
-
- result->tv_sec = x->tv_sec - y->tv_sec;
- result->tv_usec = x->tv_usec - y->tv_usec;
-}
-
-static int calc_root_size(struct btrfs_root *tree_root, struct btrfs_key *key,
- int find_inline)
-{
- struct btrfs_root *root;
- struct btrfs_path *path;
- struct rb_node *n;
- struct timeval start, end, diff = {0};
- struct root_stats stat;
- int level;
- int ret = 0;
- int size_fail = 0;
-
- root = btrfs_read_fs_root(tree_root->fs_info, key);
- if (IS_ERR(root)) {
- fprintf(stderr, "Failed to read root %Lu\n", key->objectid);
- return 1;
- }
-
- path = btrfs_alloc_path();
- if (!path) {
- fprintf(stderr, "Could not allocate path\n");
- return 1;
- }
-
- memset(&stat, 0, sizeof(stat));
- level = btrfs_header_level(root->node);
- stat.lowest_bytenr = btrfs_header_bytenr(root->node);
- stat.highest_bytenr = stat.lowest_bytenr;
- stat.min_cluster_size = (u64)-1;
- stat.max_cluster_size = root->leafsize;
- path->nodes[level] = root->node;
- if (gettimeofday(&start, NULL)) {
- fprintf(stderr, "Error getting time: %d\n", errno);
- goto out;
- }
- if (!level) {
- ret = walk_leaf(root, path, &stat, find_inline);
- if (ret)
- goto out;
- goto out_print;
- }
-
- ret = walk_nodes(root, path, &stat, level, find_inline);
- if (ret)
- goto out;
- if (gettimeofday(&end, NULL)) {
- fprintf(stderr, "Error getting time: %d\n", errno);
- goto out;
- }
- timeval_subtract(&diff, &end, &start);
-out_print:
- if (stat.min_cluster_size == (u64)-1) {
- stat.min_cluster_size = 0;
- stat.total_clusters = 1;
- }
-
- if (no_pretty || size_fail) {
- printf("\tTotal size: %Lu\n", stat.total_bytes);
- printf("\t\tInline data: %Lu\n", stat.total_inline);
- printf("\tTotal seeks: %Lu\n", stat.total_seeks);
- printf("\t\tForward seeks: %Lu\n", stat.forward_seeks);
- printf("\t\tBackward seeks: %Lu\n", stat.backward_seeks);
- printf("\t\tAvg seek len: %llu\n", stat.total_seeks ?
- stat.total_seek_len / stat.total_seeks : 0);
- print_seek_histogram(&stat);
- printf("\tTotal clusters: %Lu\n", stat.total_clusters);
- printf("\t\tAvg cluster size: %Lu\n", stat.total_cluster_size /
- stat.total_clusters);
- printf("\t\tMin cluster size: %Lu\n", stat.min_cluster_size);
- printf("\t\tMax cluster size: %Lu\n", stat.max_cluster_size);
- printf("\tTotal disk spread: %Lu\n", stat.highest_bytenr -
- stat.lowest_bytenr);
- printf("\tTotal read time: %d s %d us\n", (int)diff.tv_sec,
- (int)diff.tv_usec);
- printf("\tLevels: %d\n", level + 1);
- } else {
- printf("\tTotal size: %s\n", pretty_size(stat.total_bytes));
- printf("\t\tInline data: %s\n", pretty_size(stat.total_inline));
- printf("\tTotal seeks: %Lu\n", stat.total_seeks);
- printf("\t\tForward seeks: %Lu\n", stat.forward_seeks);
- printf("\t\tBackward seeks: %Lu\n", stat.backward_seeks);
- printf("\t\tAvg seek len: %s\n", stat.total_seeks ?
- pretty_size(stat.total_seek_len / stat.total_seeks) :
- pretty_size(0));
- print_seek_histogram(&stat);
- printf("\tTotal clusters: %Lu\n", stat.total_clusters);
- printf("\t\tAvg cluster size: %s\n",
- pretty_size((stat.total_cluster_size /
- stat.total_clusters)));
- printf("\t\tMin cluster size: %s\n",
- pretty_size(stat.min_cluster_size));
- printf("\t\tMax cluster size: %s\n",
- pretty_size(stat.max_cluster_size));
- printf("\tTotal disk spread: %s\n",
- pretty_size(stat.highest_bytenr -
- stat.lowest_bytenr));
- printf("\tTotal read time: %d s %d us\n", (int)diff.tv_sec,
- (int)diff.tv_usec);
- printf("\tLevels: %d\n", level + 1);
- }
-out:
- while ((n = rb_first(&stat.seek_root)) != NULL) {
- struct seek *seek = rb_entry(n, struct seek, n);
- rb_erase(n, &stat.seek_root);
- free(seek);
- }
-
- /*
- * We only use path to save node data in iterating,
- * without holding eb's ref_cnt in path.
- * Don't use btrfs_free_path() here, it will free these
- * eb again, and cause many problems, as negative ref_cnt
- * or invalid memory access.
- */
- free(path);
- return ret;
-}
-
-static void usage(void)
-{
- fprintf(stderr, "Usage: calc-size [-v] [-b] <device>\n");
-}
+#include "commands.h"
+#include "cmds-inspect-tree-stats.h"
int main(int argc, char **argv)
{
- struct btrfs_key key;
- struct btrfs_root *root;
- int opt;
- int ret = 0;
-
- while ((opt = getopt(argc, argv, "vb")) != -1) {
- switch (opt) {
- case 'v':
- verbose++;
- break;
- case 'b':
- no_pretty = 1;
- break;
- default:
- usage();
- exit(1);
- }
- }
-
- set_argv0(argv);
- argc = argc - optind;
- if (check_argc_min(argc, 1)) {
- usage();
- exit(1);
- }
+ int ret;
- /*
- if ((ret = check_mounted(argv[optind])) < 0) {
- fprintf(stderr, "Could not check mount status: %d\n", ret);
- if (ret == -EACCES)
- fprintf(stderr, "Maybe you need to run as root?\n");
- return ret;
- } else if (ret) {
- fprintf(stderr, "%s is currently mounted. Aborting.\n",
- argv[optind]);
- return -EBUSY;
- }
- */
+ if (argc > 1 && !strcmp(argv[1], "--help"))
+ usage(cmd_inspect_tree_stats_usage);
- root = open_ctree(argv[optind], 0, 0);
- if (!root) {
- fprintf(stderr, "Couldn't open ctree\n");
- exit(1);
- }
+ ret = cmd_inspect_tree_stats(argc, argv);
- printf("Calculating size of root tree\n");
- key.objectid = BTRFS_ROOT_TREE_OBJECTID;
- ret = calc_root_size(root, &key, 0);
- if (ret)
- goto out;
-
- printf("Calculating size of extent tree\n");
- key.objectid = BTRFS_EXTENT_TREE_OBJECTID;
- ret = calc_root_size(root, &key, 0);
- if (ret)
- goto out;
-
- printf("Calculating size of csum tree\n");
- key.objectid = BTRFS_CSUM_TREE_OBJECTID;
- ret = calc_root_size(root, &key, 0);
- if (ret)
- goto out;
-
- key.objectid = BTRFS_FS_TREE_OBJECTID;
- key.offset = (u64)-1;
- printf("Calculatin' size of fs tree\n");
- ret = calc_root_size(root, &key, 1);
- if (ret)
- goto out;
-out:
- close_ctree(root);
btrfs_close_all_devices();
+
return ret;
}
diff --git a/btrfs-completion b/btrfs-completion
index a34191bd..3ede77b6 100644
--- a/btrfs-completion
+++ b/btrfs-completion
@@ -20,13 +20,13 @@ _btrfs_mnts()
COMPREPLY+=( $( compgen -W "$MNTS" -- "$cur" ) )
}
-_btrfs()
+_btrfs()
{
local cur prev words cword
_init_completion || return
COMPREPLY=()
-
+
local cmd=${words[1]}
commands='subvolume filesystem balance device scrub check rescue restore inspect-internal property send receive quota qgroup replace help version'
@@ -36,7 +36,7 @@ _btrfs()
commands_device='scan add delete remove ready stats usage'
commands_scrub='start cancel resume status'
commands_rescue='chunk-recover super-recover'
- commands_inspect_internal='inode-resolve logical-resolve subvolid-resolve rootid min-dev-size'
+ commands_inspect_internal='inode-resolve logical-resolve subvolid-resolve rootid min-dev-size dump-tree dump-super tree-stats'
commands_property='get set list'
commands_quota='enable disable rescan'
commands_qgroup='assign remove create destroy show limit'
@@ -146,7 +146,7 @@ _btrfs()
fi
_filedir -d
- return 0
+ return 0
}
complete -F _btrfs btrfs
diff --git a/btrfs-convert.c b/btrfs-convert.c
index 4baa68ec..b49775cc 100644
--- a/btrfs-convert.c
+++ b/btrfs-convert.c
@@ -2600,8 +2600,8 @@ static int do_convert(const char *devname, int datacsum, int packing, int noxatt
}
memset(root->fs_info->super_copy->label, 0, BTRFS_LABEL_SIZE);
if (copylabel == 1) {
- strncpy(root->fs_info->super_copy->label,
- cctx.volume_name, BTRFS_LABEL_SIZE);
+ __strncpy_null(root->fs_info->super_copy->label,
+ cctx.volume_name, BTRFS_LABEL_SIZE - 1);
fprintf(stderr, "copy label '%s'\n",
root->fs_info->super_copy->label);
} else if (copylabel == -1) {
@@ -2781,7 +2781,7 @@ static int do_rollback(const char *devname)
goto fail;
} else if (ret < 0) {
fprintf(stderr,
- "ERROR: unable to open ext2_subvol, id=%llu: %s\n",
+ "ERROR: unable to open ext2_saved, id=%llu: %s\n",
(unsigned long long)key.objectid, strerror(-ret));
goto fail;
}
@@ -3116,8 +3116,7 @@ int main(int argc, char *argv[])
"WARNING: label too long, trimmed to %d bytes\n",
BTRFS_LABEL_SIZE - 1);
}
- strncpy(fslabel, optarg, BTRFS_LABEL_SIZE - 1);
- fslabel[BTRFS_LABEL_SIZE - 1] = 0;
+ __strncpy_null(fslabel, optarg, BTRFS_LABEL_SIZE - 1);
break;
case 'L':
copylabel = 1;
@@ -3165,9 +3164,8 @@ int main(int argc, char *argv[])
return c != GETOPT_VAL_HELP;
}
}
- argc = argc - optind;
set_argv0(argv);
- if (check_argc_exact(argc, 1)) {
+ if (check_argc_exact(argc - optind, 1)) {
print_usage();
return 1;
}
diff --git a/btrfs-corrupt-block.c b/btrfs-corrupt-block.c
index be5cd7ea..66d93e59 100644
--- a/btrfs-corrupt-block.c
+++ b/btrfs-corrupt-block.c
@@ -162,7 +162,7 @@ static int corrupt_keys_in_block(struct btrfs_root *root, u64 bytenr)
{
struct extent_buffer *eb;
- eb = read_tree_block(root, bytenr, root->leafsize, 0);
+ eb = read_tree_block(root, bytenr, root->nodesize, 0);
if (!extent_buffer_uptodate(eb))
return -EIO;;
@@ -289,7 +289,7 @@ static void btrfs_corrupt_extent_tree(struct btrfs_trans_handle *trans,
struct extent_buffer *next;
next = read_tree_block(root, btrfs_node_blockptr(eb, i),
- root->leafsize,
+ root->nodesize,
btrfs_node_ptr_generation(eb, i));
if (!extent_buffer_uptodate(next))
continue;
@@ -700,7 +700,7 @@ static int corrupt_metadata_block(struct btrfs_root *root, u64 block,
return -EINVAL;
}
- eb = read_tree_block(root, block, root->leafsize, 0);
+ eb = read_tree_block(root, block, root->nodesize, 0);
if (!extent_buffer_uptodate(eb)) {
fprintf(stderr, "Couldn't read in tree block %s\n", field);
return -EINVAL;
@@ -1012,7 +1012,7 @@ out:
return ret;
}
-int main(int ac, char **av)
+int main(int argc, char **argv)
{
struct cache_tree root_cache;
struct btrfs_key key;
@@ -1069,7 +1069,7 @@ int main(int ac, char **av)
{ NULL, 0, NULL, 0 }
};
- c = getopt_long(ac, av, "l:c:b:eEkuUi:f:x:m:K:IDdr:C:",
+ c = getopt_long(argc, argv, "l:c:b:eEkuUi:f:x:m:K:IDdr:C:",
long_options, NULL);
if (c < 0)
break;
@@ -1141,11 +1141,10 @@ int main(int ac, char **av)
print_usage(c != GETOPT_VAL_HELP);
}
}
- set_argv0(av);
- ac = ac - optind;
- if (check_argc_min(ac, 1))
+ set_argv0(argv);
+ if (check_argc_min(argc - optind, 1))
print_usage(1);
- dev = av[optind];
+ dev = argv[optind];
radix_tree_init();
cache_tree_init(&root_cache);
diff --git a/btrfs-debug-tree.c b/btrfs-debug-tree.c
index 266176f3..a645b406 100644
--- a/btrfs-debug-tree.c
+++ b/btrfs-debug-tree.c
@@ -16,447 +16,26 @@
* Boston, MA 021110-1307, USA.
*/
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <uuid/uuid.h>
-#include <getopt.h>
-
-#include "kerncompat.h"
-#include "radix-tree.h"
-#include "ctree.h"
#include "disk-io.h"
-#include "print-tree.h"
-#include "transaction.h"
#include "volumes.h"
#include "utils.h"
+#include "commands.h"
+#include "cmds-inspect-dump-tree.h"
-static int print_usage(int ret)
-{
- fprintf(stderr, "usage: btrfs-debug-tree [-e] [-d] [-r] [-R] [-u]\n");
- fprintf(stderr, " [-b block_num ] device\n");
- fprintf(stderr, "\t-e : print detailed extents info\n");
- fprintf(stderr, "\t-d : print info of btrfs device and root tree dirs"
- " only\n");
- fprintf(stderr, "\t-r : print info of roots only\n");
- fprintf(stderr, "\t-R : print info of roots and root backups\n");
- fprintf(stderr, "\t-u : print info of uuid tree only\n");
- fprintf(stderr, "\t-b block_num : print info of the specified block"
- " only\n");
- fprintf(stderr,
- "\t-t tree_id : print only the tree with the given id\n");
- fprintf(stderr, "%s\n", PACKAGE_STRING);
- exit(ret);
-}
-
-static void print_extents(struct btrfs_root *root, struct extent_buffer *eb)
-{
- int i;
- u32 nr;
- u32 size;
-
- if (!eb)
- return;
-
- if (btrfs_is_leaf(eb)) {
- btrfs_print_leaf(root, eb);
- return;
- }
-
- size = btrfs_level_size(root, btrfs_header_level(eb) - 1);
- nr = btrfs_header_nritems(eb);
- for (i = 0; i < nr; i++) {
- struct extent_buffer *next = read_tree_block(root,
- btrfs_node_blockptr(eb, i),
- size,
- btrfs_node_ptr_generation(eb, i));
- if (!extent_buffer_uptodate(next))
- continue;
- if (btrfs_is_leaf(next) &&
- btrfs_header_level(eb) != 1)
- BUG();
- if (btrfs_header_level(next) !=
- btrfs_header_level(eb) - 1)
- BUG();
- print_extents(root, next);
- free_extent_buffer(next);
- }
-}
-
-static void print_old_roots(struct btrfs_super_block *super)
-{
- struct btrfs_root_backup *backup;
- int i;
-
- for (i = 0; i < BTRFS_NUM_BACKUP_ROOTS; i++) {
- backup = super->super_roots + i;
- printf("btrfs root backup slot %d\n", i);
- printf("\ttree root gen %llu block %llu\n",
- (unsigned long long)btrfs_backup_tree_root_gen(backup),
- (unsigned long long)btrfs_backup_tree_root(backup));
-
- printf("\t\textent root gen %llu block %llu\n",
- (unsigned long long)btrfs_backup_extent_root_gen(backup),
- (unsigned long long)btrfs_backup_extent_root(backup));
-
- printf("\t\tchunk root gen %llu block %llu\n",
- (unsigned long long)btrfs_backup_chunk_root_gen(backup),
- (unsigned long long)btrfs_backup_chunk_root(backup));
-
- printf("\t\tdevice root gen %llu block %llu\n",
- (unsigned long long)btrfs_backup_dev_root_gen(backup),
- (unsigned long long)btrfs_backup_dev_root(backup));
-
- printf("\t\tcsum root gen %llu block %llu\n",
- (unsigned long long)btrfs_backup_csum_root_gen(backup),
- (unsigned long long)btrfs_backup_csum_root(backup));
-
- printf("\t\tfs root gen %llu block %llu\n",
- (unsigned long long)btrfs_backup_fs_root_gen(backup),
- (unsigned long long)btrfs_backup_fs_root(backup));
-
- printf("\t\t%llu used %llu total %llu devices\n",
- (unsigned long long)btrfs_backup_bytes_used(backup),
- (unsigned long long)btrfs_backup_total_bytes(backup),
- (unsigned long long)btrfs_backup_num_devices(backup));
- }
-}
-
-int main(int ac, char **av)
+int main(int argc, char **argv)
{
- struct btrfs_root *root;
- struct btrfs_fs_info *info;
- struct btrfs_path path;
- struct btrfs_key key;
- struct btrfs_root_item ri;
- struct extent_buffer *leaf;
- struct btrfs_disk_key disk_key;
- struct btrfs_key found_key;
- char uuidbuf[BTRFS_UUID_UNPARSED_SIZE];
int ret;
- int slot;
- int extent_only = 0;
- int device_only = 0;
- int uuid_tree_only = 0;
- int roots_only = 0;
- int root_backups = 0;
- u64 block_only = 0;
- struct btrfs_root *tree_root_scan;
- u64 tree_id = 0;
- radix_tree_init();
-
- while(1) {
- int c;
- static const struct option long_options[] = {
- { "help", no_argument, NULL, GETOPT_VAL_HELP},
- { NULL, 0, NULL, 0 }
- };
-
- c = getopt_long(ac, av, "deb:rRut:", long_options, NULL);
- if (c < 0)
- break;
- switch(c) {
- case 'e':
- extent_only = 1;
- break;
- case 'd':
- device_only = 1;
- break;
- case 'r':
- roots_only = 1;
- break;
- case 'u':
- uuid_tree_only = 1;
- break;
- case 'R':
- roots_only = 1;
- root_backups = 1;
- break;
- case 'b':
- block_only = arg_strtou64(optarg);
- break;
- case 't':
- tree_id = arg_strtou64(optarg);
- break;
- case GETOPT_VAL_HELP:
- default:
- print_usage(c != GETOPT_VAL_HELP);
- }
- }
- set_argv0(av);
- ac = ac - optind;
- if (check_argc_exact(ac, 1))
- print_usage(1);
-
- ret = check_arg_type(av[optind]);
- if (ret != BTRFS_ARG_BLKDEV && ret != BTRFS_ARG_REG) {
- fprintf(stderr, "'%s' is not a block device or regular file\n",
- av[optind]);
- exit(1);
- }
-
- info = open_ctree_fs_info(av[optind], 0, 0, OPEN_CTREE_PARTIAL);
- if (!info) {
- fprintf(stderr, "unable to open %s\n", av[optind]);
- exit(1);
- }
-
- root = info->fs_root;
- if (!root) {
- fprintf(stderr, "unable to open %s\n", av[optind]);
- exit(1);
- }
-
- if (block_only) {
- leaf = read_tree_block(root,
- block_only,
- root->leafsize, 0);
-
- if (extent_buffer_uptodate(leaf) &&
- btrfs_header_level(leaf) != 0) {
- free_extent_buffer(leaf);
- leaf = NULL;
- }
-
- if (!leaf) {
- leaf = read_tree_block(root,
- block_only,
- root->nodesize, 0);
- }
- if (!extent_buffer_uptodate(leaf)) {
- fprintf(stderr, "failed to read %llu\n",
- (unsigned long long)block_only);
- goto close_root;
- }
- btrfs_print_tree(root, leaf, 0);
- free_extent_buffer(leaf);
- goto close_root;
- }
-
- if (!(extent_only || uuid_tree_only || tree_id)) {
- if (roots_only) {
- printf("root tree: %llu level %d\n",
- (unsigned long long)info->tree_root->node->start,
- btrfs_header_level(info->tree_root->node));
- printf("chunk tree: %llu level %d\n",
- (unsigned long long)info->chunk_root->node->start,
- btrfs_header_level(info->chunk_root->node));
- } else {
- if (info->tree_root->node) {
- printf("root tree\n");
- btrfs_print_tree(info->tree_root,
- info->tree_root->node, 1);
- }
+ set_argv0(argv);
- if (info->chunk_root->node) {
- printf("chunk tree\n");
- btrfs_print_tree(info->chunk_root,
- info->chunk_root->node, 1);
- }
- }
- }
- tree_root_scan = info->tree_root;
+ if (argc > 1 && !strcmp(argv[1], "--help"))
+ usage(cmd_inspect_dump_tree_usage);
- btrfs_init_path(&path);
-again:
- if (!extent_buffer_uptodate(tree_root_scan->node))
- goto no_node;
-
- /*
- * Tree's that are not pointed by the tree of tree roots
- */
- if (tree_id && tree_id == BTRFS_ROOT_TREE_OBJECTID) {
- if (!info->tree_root->node) {
- error("cannot print root tree, invalid pointer");
- goto no_node;
- }
- printf("root tree\n");
- btrfs_print_tree(info->tree_root, info->tree_root->node, 1);
- goto no_node;
- }
-
- if (tree_id && tree_id == BTRFS_CHUNK_TREE_OBJECTID) {
- if (!info->chunk_root->node) {
- error("cannot print chunk tree, invalid pointer");
- goto no_node;
- }
- printf("chunk tree\n");
- btrfs_print_tree(info->chunk_root, info->chunk_root->node, 1);
- goto no_node;
- }
-
- key.offset = 0;
- key.objectid = 0;
- btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY);
- ret = btrfs_search_slot(NULL, tree_root_scan, &key, &path, 0, 0);
- BUG_ON(ret < 0);
- while(1) {
- leaf = path.nodes[0];
- slot = path.slots[0];
- if (slot >= btrfs_header_nritems(leaf)) {
- ret = btrfs_next_leaf(tree_root_scan, &path);
- if (ret != 0)
- break;
- leaf = path.nodes[0];
- slot = path.slots[0];
- }
- btrfs_item_key(leaf, &disk_key, path.slots[0]);
- btrfs_disk_key_to_cpu(&found_key, &disk_key);
- if (btrfs_key_type(&found_key) == BTRFS_ROOT_ITEM_KEY) {
- unsigned long offset;
- struct extent_buffer *buf;
- int skip = extent_only | device_only | uuid_tree_only;
-
- offset = btrfs_item_ptr_offset(leaf, slot);
- read_extent_buffer(leaf, &ri, offset, sizeof(ri));
- buf = read_tree_block(tree_root_scan,
- btrfs_root_bytenr(&ri),
- btrfs_level_size(tree_root_scan,
- btrfs_root_level(&ri)),
- 0);
- if (!extent_buffer_uptodate(buf))
- goto next;
- if (tree_id && found_key.objectid != tree_id) {
- free_extent_buffer(buf);
- goto next;
- }
-
- switch(found_key.objectid) {
- case BTRFS_ROOT_TREE_OBJECTID:
- if (!skip)
- printf("root");
- break;
- case BTRFS_EXTENT_TREE_OBJECTID:
- if (!device_only && !uuid_tree_only)
- skip = 0;
- if (!skip)
- printf("extent");
- break;
- case BTRFS_CHUNK_TREE_OBJECTID:
- if (!skip) {
- printf("chunk");
- }
- break;
- case BTRFS_DEV_TREE_OBJECTID:
- if (!uuid_tree_only)
- skip = 0;
- if (!skip)
- printf("device");
- break;
- case BTRFS_FS_TREE_OBJECTID:
- if (!skip) {
- printf("fs");
- }
- break;
- case BTRFS_ROOT_TREE_DIR_OBJECTID:
- skip = 0;
- printf("directory");
- break;
- case BTRFS_CSUM_TREE_OBJECTID:
- if (!skip) {
- printf("checksum");
- }
- break;
- case BTRFS_ORPHAN_OBJECTID:
- if (!skip) {
- printf("orphan");
- }
- break;
- case BTRFS_TREE_LOG_OBJECTID:
- if (!skip) {
- printf("log");
- }
- break;
- case BTRFS_TREE_LOG_FIXUP_OBJECTID:
- if (!skip) {
- printf("log fixup");
- }
- break;
- case BTRFS_TREE_RELOC_OBJECTID:
- if (!skip) {
- printf("reloc");
- }
- break;
- case BTRFS_DATA_RELOC_TREE_OBJECTID:
- if (!skip) {
- printf("data reloc");
- }
- break;
- case BTRFS_EXTENT_CSUM_OBJECTID:
- if (!skip) {
- printf("extent checksum");
- }
- break;
- case BTRFS_QUOTA_TREE_OBJECTID:
- if (!skip) {
- printf("quota");
- }
- break;
- case BTRFS_UUID_TREE_OBJECTID:
- if (!extent_only && !device_only)
- skip = 0;
- if (!skip)
- printf("uuid");
- break;
- case BTRFS_FREE_SPACE_TREE_OBJECTID:
- if (!skip)
- printf("free space");
- break;
- case BTRFS_MULTIPLE_OBJECTIDS:
- if (!skip) {
- printf("multiple");
- }
- break;
- default:
- if (!skip) {
- printf("file");
- }
- }
- if (extent_only && !skip) {
- print_extents(tree_root_scan, buf);
- } else if (!skip) {
- printf(" tree ");
- btrfs_print_key(&disk_key);
- if (roots_only) {
- printf(" %llu level %d\n",
- (unsigned long long)buf->start,
- btrfs_header_level(buf));
- } else {
- printf(" \n");
- btrfs_print_tree(tree_root_scan, buf, 1);
- }
- }
- free_extent_buffer(buf);
- }
-next:
- path.slots[0]++;
- }
-no_node:
- btrfs_release_path(&path);
-
- if (tree_root_scan == info->tree_root &&
- info->log_root_tree) {
- tree_root_scan = info->log_root_tree;
- goto again;
- }
-
- if (extent_only || device_only || uuid_tree_only)
- goto close_root;
+ radix_tree_init();
- if (root_backups)
- print_old_roots(info->super_copy);
+ ret = cmd_inspect_dump_tree(argc, argv);
- printf("total bytes %llu\n",
- (unsigned long long)btrfs_super_total_bytes(info->super_copy));
- printf("bytes used %llu\n",
- (unsigned long long)btrfs_super_bytes_used(info->super_copy));
- uuidbuf[BTRFS_UUID_UNPARSED_SIZE - 1] = '\0';
- uuid_unparse(info->super_copy->fsid, uuidbuf);
- printf("uuid %s\n", uuidbuf);
- printf("%s\n", PACKAGE_STRING);
-close_root:
- ret = close_ctree(root);
btrfs_close_all_devices();
+
return ret;
}
diff --git a/btrfs-debugfs b/btrfs-debugfs
index cf1d285c..08e3ec63 100755
--- a/btrfs-debugfs
+++ b/btrfs-debugfs
@@ -4,7 +4,7 @@
# LGPLv2 license
# Copyright Facebook 2014
-import sys,os,struct,fcntl,ctypes,stat
+import sys,os,struct,fcntl,ctypes,stat,argparse
# helpers for max ints
maxu64 = (1L << 64) - 1
@@ -65,6 +65,11 @@ BTRFS_DEV_STATS_KEY = 249
BTRFS_DEV_REPLACE_KEY = 250
BTRFS_STRING_ITEM_KEY = 253
+# store information about which extents are in use, and reference counts
+BTRFS_EXTENT_TREE_OBJECTID = 2
+
+BTRFS_BLOCK_GROUP_DATA = (1 << 0)
+
# in the kernel sources, this is flattened
# btrfs_ioctl_search_args_v2. It includes both the btrfs_ioctl_search_key
# and the buffer. We're using a 64K buffer size.
@@ -121,6 +126,13 @@ class btrfs_file_extent_item(ctypes.LittleEndianStructure):
("num_bytes", ctypes.c_ulonglong),
]
+class btrfs_block_group_item(ctypes.LittleEndianStructure):
+ _pack_ = 1
+ _fields_ = [ ("used", ctypes.c_ulonglong),
+ ("chunk_objectid", ctypes.c_ulonglong),
+ ("flags", ctypes.c_ulonglong),
+ ]
+
class btrfs_ioctl_search():
def __init__(self):
self.args = btrfs_ioctl_search_args()
@@ -288,9 +300,102 @@ def print_file_extents(filename):
float(st.st_size) / float(total_on_disk))
return 0
-if len(sys.argv) == 1:
- sys.stderr.write("Usage: btrfs-debug filename ...\n")
- sys.exit(1)
+def print_block_groups(mountpoint):
+ s = btrfs_ioctl_search()
+
+ s.args.min_type = BTRFS_BLOCK_GROUP_ITEM_KEY
+ s.args.max_type = BTRFS_BLOCK_GROUP_ITEM_KEY
+ s.args.tree_id = BTRFS_EXTENT_TREE_OBJECTID
+
+ min_used = maxu64
+ free_of_min_used = 0
+ bg_of_min_used = 0
+ total_free = 0
+
+ try:
+ fd = os.open(mountpoint, os.O_RDONLY)
+ st = os.fstat(fd)
+ except Exception, e:
+ sys.stderr.write("Failed to open %s (%s)\n" % (mountpoint, e))
+ return -1
+
+ while True:
+ try:
+ s.search(fd)
+ except Exception, e:
+ sys.stderr.write("Search ioctl failed for %s (%s)\n" % (mountpoint, e))
+ return -1
+
+ if s.args.nr_items == 0:
+ break
+
+ # p is the results buffer from kernel
+ p = ctypes.addressof(s.args.buf)
+ header = btrfs_ioctl_search_header()
+ header_size = ctypes.sizeof(header)
+ h = ctypes.addressof(header)
+ p_left = args_buffer_size
+
+ for x in xrange(0, s.args.nr_items):
+ # for each itme, copy the header from the buffer into
+ # our header struct
+ ctypes.memmove(h, p, header_size)
+ p += header_size
+ p_left -= header_size
+
+ # this would be a kernel bug it shouldn't be sending malformed
+ # items
+ if p_left <= 0:
+ break
+
+ if header.type == BTRFS_BLOCK_GROUP_ITEM_KEY:
+ bg = btrfs_block_group_item()
+
+ # this would be a kernel bug
+ if p_left < ctypes.sizeof(bg):
+ break
+
+ ctypes.memmove(ctypes.addressof(bg), p, ctypes.sizeof(bg))
+ if bg.flags & BTRFS_BLOCK_GROUP_DATA:
+ print "block group offset %Lu len %Lu used %Lu chunk_objectid %Lu flags %Lu usage %.2f" %\
+ (header.objectid, header.offset, bg.used, bg.chunk_objectid, bg.flags, float(bg.used) / float(header.offset))
+
+ total_free += (header.offset - bg.used)
+ if min_used >= bg.used:
+ min_used = bg.used
+ free_of_min_used = (header.offset - bg.used)
+ bg_of_min_used = header.objectid
+
+ p += header.len
+ p_left -= header.len
+ if p_left <= 0:
+ break
+
+ s.args.min_objectid = header.objectid
+
+ if s.args.min_objectid < maxu64:
+ s.args.min_objectid += 1
+ if s.args.min_objectid > s.args.max_objectid:
+ break
+
+ print "total_free %Lu min_used %Lu free_of_min_used %Lu block_group_of_min_used %Lu" %\
+ (total_free, min_used, free_of_min_used, bg_of_min_used)
+ if (total_free - free_of_min_used) >= min_used:
+ print "balance block group (%Lu) can reduce the number of data block group" % bg_of_min_used
+
+ return 0
+
+# main
+parser = argparse.ArgumentParser()
+parser.add_argument('path', nargs='+')
+parser.add_argument('-b', '--block-group', action='store_const', const=1, help='get block group information, use mountpoint as "path"')
+parser.add_argument('-f', '--file', action='store_const', const=1, help='get file mapping, use filepath')
+
+args = parser.parse_args()
-for f in sys.argv[1:]:
- print_file_extents(f)
+if args.block_group:
+ for i in args.path[0:]:
+ print_block_groups(i)
+elif args.file:
+ for f in args.path[0:]:
+ print_file_extents(f)
diff --git a/btrfs-find-root.c b/btrfs-find-root.c
index 2d5bbb2a..ed1540a8 100644
--- a/btrfs-find-root.c
+++ b/btrfs-find-root.c
@@ -185,13 +185,12 @@ int main(int argc, char **argv)
}
set_argv0(argv);
- argc = argc - optind;
- if (check_argc_min(argc, 1)) {
+ if (check_argc_min(argc - optind, 1)) {
usage();
exit(1);
}
- fs_info = open_ctree_fs_info(argv[optind], 0, 0,
+ fs_info = open_ctree_fs_info(argv[optind], 0, 0, 0,
OPEN_CTREE_CHUNK_ROOT_ONLY |
OPEN_CTREE_IGNORE_CHUNK_TREE_ERROR);
if (!fs_info) {
diff --git a/btrfs-fragments.c b/btrfs-fragments.c
index 17768c3f..9db1861e 100644
--- a/btrfs-fragments.c
+++ b/btrfs-fragments.c
@@ -379,8 +379,7 @@ out_close:
return ret;
}
-void
-usage(void)
+void fragments_usage(void)
{
printf("usage: btrfs-fragments [options] <path>\n");
printf(" -c use color\n");
@@ -423,16 +422,13 @@ int main(int argc, char **argv)
break;
case 'h':
default:
- usage();
+ fragments_usage();
}
}
set_argv0(argv);
- argc = argc - optind;
- if (check_argc_min(argc, 1)) {
- usage();
- exit(1);
- }
+ if (check_argc_min(argc - optind, 1))
+ fragments_usage();
path = argv[optind++];
diff --git a/btrfs-image.c b/btrfs-image.c
index c7fa18fb..5c4c6fac 100644
--- a/btrfs-image.c
+++ b/btrfs-image.c
@@ -133,7 +133,7 @@ struct mdrestore_struct {
struct list_head list;
struct list_head overlapping_chunks;
size_t num_items;
- u32 leafsize;
+ u32 nodesize;
u64 devid;
u64 alloced_chunks;
u64 last_physical_offset;
@@ -1073,7 +1073,7 @@ static int copy_tree_blocks(struct btrfs_root *root, struct extent_buffer *eb,
int i = 0;
int ret;
- ret = add_extent(btrfs_header_bytenr(eb), root->leafsize, metadump, 0);
+ ret = add_extent(btrfs_header_bytenr(eb), root->nodesize, metadump, 0);
if (ret) {
fprintf(stderr, "Error adding metadata block\n");
return ret;
@@ -1091,7 +1091,7 @@ 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->leafsize, 0);
+ tmp = read_tree_block(root, bytenr, root->nodesize, 0);
if (!extent_buffer_uptodate(tmp)) {
fprintf(stderr,
"Error reading log root block\n");
@@ -1103,7 +1103,7 @@ 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->leafsize, 0);
+ tmp = read_tree_block(root, bytenr, root->nodesize, 0);
if (!extent_buffer_uptodate(tmp)) {
fprintf(stderr, "Error reading log block\n");
return -EIO;
@@ -1255,7 +1255,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->leafsize;
+ num_bytes = extent_root->nodesize;
else
num_bytes = key.offset;
@@ -1321,8 +1321,6 @@ static int create_metadump(const char *input, FILE *out, int num_threads,
return -EIO;
}
- BUG_ON(root->nodesize != root->leafsize);
-
ret = metadump_init(&metadump, root, out, num_threads,
compress_level, sanitize);
if (ret) {
@@ -1550,16 +1548,16 @@ static int fixup_chunk_tree_block(struct mdrestore_struct *mdres,
u64 bytenr = async->start;
int i;
- if (size_left % mdres->leafsize)
+ if (size_left % mdres->nodesize)
return 0;
- eb = alloc_dummy_eb(bytenr, mdres->leafsize);
+ eb = alloc_dummy_eb(bytenr, mdres->nodesize);
if (!eb)
return -ENOMEM;
while (size_left) {
eb->start = bytenr;
- memcpy(eb->data, buffer, mdres->leafsize);
+ memcpy(eb->data, buffer, mdres->nodesize);
if (btrfs_header_bytenr(eb) != bytenr)
break;
@@ -1614,9 +1612,9 @@ static int fixup_chunk_tree_block(struct mdrestore_struct *mdres,
memcpy(buffer, eb->data, eb->len);
csum_block(buffer, eb->len);
next:
- size_left -= mdres->leafsize;
- buffer += mdres->leafsize;
- bytenr += mdres->leafsize;
+ size_left -= mdres->nodesize;
+ buffer += mdres->nodesize;
+ bytenr += mdres->nodesize;
}
free(eb);
@@ -1687,7 +1685,7 @@ static void *restore_worker(void *data)
int err = 0;
pthread_mutex_lock(&mdres->mutex);
- while (!mdres->leafsize || list_empty(&mdres->list)) {
+ while (!mdres->nodesize || list_empty(&mdres->list)) {
if (mdres->done) {
pthread_mutex_unlock(&mdres->mutex);
goto out;
@@ -1859,7 +1857,7 @@ static int fill_mdres_info(struct mdrestore_struct *mdres,
int ret;
/* We've already been initialized */
- if (mdres->leafsize)
+ if (mdres->nodesize)
return 0;
if (mdres->compress_method == COMPRESS_ZLIB) {
@@ -1881,7 +1879,7 @@ static int fill_mdres_info(struct mdrestore_struct *mdres,
}
super = (struct btrfs_super_block *)outbuf;
- mdres->leafsize = btrfs_super_leafsize(super);
+ mdres->nodesize = btrfs_super_nodesize(super);
memcpy(mdres->fsid, super->fsid, BTRFS_FSID_SIZE);
memcpy(mdres->uuid, super->dev_item.uuid,
BTRFS_UUID_SIZE);
@@ -1987,18 +1985,18 @@ static int read_chunk_block(struct mdrestore_struct *mdres, u8 *buffer,
int ret = 0;
int i;
- eb = alloc_dummy_eb(bytenr, mdres->leafsize);
+ eb = alloc_dummy_eb(bytenr, mdres->nodesize);
if (!eb) {
ret = -ENOMEM;
goto out;
}
while (item_bytenr != bytenr) {
- buffer += mdres->leafsize;
- item_bytenr += mdres->leafsize;
+ buffer += mdres->nodesize;
+ item_bytenr += mdres->nodesize;
}
- memcpy(eb->data, buffer, mdres->leafsize);
+ memcpy(eb->data, buffer, mdres->nodesize);
if (btrfs_header_bytenr(eb) != bytenr) {
fprintf(stderr, "Eb bytenr doesn't match found bytenr\n");
ret = -EIO;
@@ -2301,7 +2299,7 @@ static int build_chunk_tree(struct mdrestore_struct *mdres,
pthread_mutex_lock(&mdres->mutex);
super = (struct btrfs_super_block *)buffer;
chunk_root_bytenr = btrfs_super_chunk_root(super);
- mdres->leafsize = btrfs_super_leafsize(super);
+ mdres->nodesize = btrfs_super_nodesize(super);
memcpy(mdres->fsid, super->fsid, BTRFS_FSID_SIZE);
memcpy(mdres->uuid, super->dev_item.uuid,
BTRFS_UUID_SIZE);
@@ -2471,7 +2469,7 @@ static int restore_metadump(const char *input, FILE *out, int old_restore,
/* NOTE: open with write mode */
if (fixup_offset) {
BUG_ON(!target);
- info = open_ctree_fs_info(target, 0, 0,
+ info = open_ctree_fs_info(target, 0, 0, 0,
OPEN_CTREE_WRITES |
OPEN_CTREE_RESTORE |
OPEN_CTREE_PARTIAL);
@@ -2735,12 +2733,11 @@ int main(int argc, char *argv[])
}
}
- argc = argc - optind;
set_argv0(argv);
- if (check_argc_min(argc, 2))
+ if (check_argc_min(argc - optind, 2))
print_usage(1);
- dev_cnt = argc - 1;
+ dev_cnt = argc - optind - 1;
if (create) {
if (old_restore) {
@@ -2818,7 +2815,7 @@ int main(int argc, char *argv[])
u64 total_devs;
int i;
- info = open_ctree_fs_info(target, 0, 0,
+ info = open_ctree_fs_info(target, 0, 0, 0,
OPEN_CTREE_PARTIAL |
OPEN_CTREE_RESTORE);
if (!info) {
diff --git a/btrfs-map-logical.c b/btrfs-map-logical.c
index 0161b5c6..fd0286d5 100644
--- a/btrfs-map-logical.c
+++ b/btrfs-map-logical.c
@@ -81,7 +81,7 @@ again:
}
logical = key.objectid;
if (key.type == BTRFS_METADATA_ITEM_KEY)
- len = fs_info->tree_root->leafsize;
+ len = fs_info->tree_root->nodesize;
else
len = key.offset;
@@ -201,7 +201,7 @@ static void print_usage(void)
exit(1);
}
-int main(int ac, char **av)
+int main(int argc, char **argv)
{
struct cache_tree root_cache;
struct btrfs_root *root;
@@ -227,7 +227,7 @@ int main(int ac, char **av)
{ NULL, 0, NULL, 0}
};
- c = getopt_long(ac, av, "l:c:o:b:", long_options, NULL);
+ c = getopt_long(argc, argv, "l:c:o:b:", long_options, NULL);
if (c < 0)
break;
switch(c) {
@@ -247,14 +247,13 @@ int main(int ac, char **av)
print_usage();
}
}
- set_argv0(av);
- ac = ac - optind;
- if (check_argc_min(ac, 1))
+ set_argv0(argv);
+ if (check_argc_min(argc - optind, 1))
print_usage();
if (logical == 0)
print_usage();
- dev = av[optind];
+ dev = argv[optind];
radix_tree_init();
cache_tree_init(&root_cache);
diff --git a/btrfs-select-super.c b/btrfs-select-super.c
index df741532..4f4ec0b6 100644
--- a/btrfs-select-super.c
+++ b/btrfs-select-super.c
@@ -37,7 +37,7 @@ static void print_usage(void)
exit(1);
}
-int main(int ac, char **av)
+int main(int argc, char **argv)
{
struct btrfs_root *root;
int ret;
@@ -46,7 +46,7 @@ int main(int ac, char **av)
while(1) {
int c;
- c = getopt(ac, av, "s:");
+ c = getopt(argc, argv, "s:");
if (c < 0)
break;
switch(c) {
@@ -64,10 +64,8 @@ int main(int ac, char **av)
print_usage();
}
}
- set_argv0(av);
- ac = ac - optind;
-
- if (check_argc_exact(ac, 1))
+ set_argv0(argv);
+ if (check_argc_exact(argc - optind, 1))
print_usage();
if (bytenr == 0) {
@@ -77,15 +75,15 @@ int main(int ac, char **av)
radix_tree_init();
- if((ret = check_mounted(av[optind])) < 0) {
- fprintf(stderr, "Could not check mount status: %s\n", strerror(-ret));
+ if((ret = check_mounted(argv[optind])) < 0) {
+ error("cannot check mount status: %s", strerror(-ret));
return ret;
} else if(ret) {
- fprintf(stderr, "%s is currently mounted. Aborting.\n", av[optind]);
+ error("%s is currently mounted, aborting", argv[optind]);
return -EBUSY;
}
- root = open_ctree(av[optind], bytenr, 1);
+ root = open_ctree(argv[optind], bytenr, 1);
if (!root) {
fprintf(stderr, "Open ctree failed\n");
diff --git a/btrfs-show-super.c b/btrfs-show-super.c
index 051bd115..3e2ceb58 100644
--- a/btrfs-show-super.c
+++ b/btrfs-show-super.c
@@ -16,527 +16,21 @@
* Boston, MA 021110-1307, USA.
*/
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <ctype.h>
-#include <uuid/uuid.h>
-#include <errno.h>
-
-#include "kerncompat.h"
-#include "ctree.h"
-#include "disk-io.h"
-#include "print-tree.h"
-#include "transaction.h"
-#include "list.h"
#include "utils.h"
-#include "crc32c.h"
-
-static void print_usage(void);
-static void dump_superblock(struct btrfs_super_block *sb, int full);
-int main(int argc, char **argv);
-static int load_and_dump_sb(char *, int fd, u64 sb_bytenr, int full, int force);
-
-
-static void print_usage(void)
-{
- fprintf(stderr,
- "usage: btrfs-show-super [-i super_mirror|-a|-f|-F] dev [dev..]\n");
- fprintf(stderr, "\t-f : print full superblock information\n");
- fprintf(stderr, "\t-a : print information of all superblocks\n");
- fprintf(stderr, "\t-i <super_mirror> : specify which mirror to print out\n");
- fprintf(stderr, "\t-F : attempt to dump superblocks with bad magic\n");
- fprintf(stderr, "\t-s <bytenr> : specify alternate superblock offset\n");
- fprintf(stderr, "%s\n", PACKAGE_STRING);
-}
+#include "commands.h"
+#include "cmds-inspect-dump-super.h"
int main(int argc, char **argv)
{
- int opt;
- int all = 0;
- int full = 0;
- int force = 0;
- char *filename;
- int fd = -1;
- int i;
- u64 arg;
- u64 sb_bytenr = btrfs_sb_offset(0);
-
- while ((opt = getopt(argc, argv, "fFai:s:")) != -1) {
- switch (opt) {
- case 'i':
- arg = arg_strtou64(optarg);
- if (arg >= BTRFS_SUPER_MIRROR_MAX) {
- fprintf(stderr,
- "Illegal super_mirror %llu\n",
- arg);
- print_usage();
- exit(1);
- }
- sb_bytenr = btrfs_sb_offset(arg);
- break;
- case 'a':
- all = 1;
- break;
- case 'f':
- full = 1;
- break;
- case 'F':
- force = 1;
- break;
- case 's':
- sb_bytenr = arg_strtou64(optarg);
- all = 0;
- break;
- default:
- print_usage();
- exit(1);
- }
- }
+ int ret;
set_argv0(argv);
- if (check_argc_min(argc - optind, 1)) {
- print_usage();
- exit(1);
- }
-
- for (i = optind; i < argc; i++) {
- filename = argv[i];
- fd = open(filename, O_RDONLY, 0666);
- if (fd < 0) {
- fprintf(stderr, "Could not open %s\n", filename);
- exit(1);
- }
-
- if (all) {
- int idx;
- for (idx = 0; idx < BTRFS_SUPER_MIRROR_MAX; idx++) {
- sb_bytenr = btrfs_sb_offset(idx);
- if (load_and_dump_sb(filename, fd,
- sb_bytenr, full, force)) {
- close(fd);
- exit(1);
- }
-
- putchar('\n');
- }
- } else {
- load_and_dump_sb(filename, fd, sb_bytenr, full, force);
- putchar('\n');
- }
- close(fd);
- }
-
- exit(0);
-}
-
-static int load_and_dump_sb(char *filename, int fd, u64 sb_bytenr, int full,
- int force)
-{
- u8 super_block_data[BTRFS_SUPER_INFO_SIZE];
- struct btrfs_super_block *sb;
- u64 ret;
-
- sb = (struct btrfs_super_block *)super_block_data;
-
- ret = pread64(fd, super_block_data, BTRFS_SUPER_INFO_SIZE, sb_bytenr);
- if (ret != BTRFS_SUPER_INFO_SIZE) {
- /* check if the disk if too short for further superblock */
- if (ret == 0 && errno == 0)
- return 0;
-
- fprintf(stderr,
- "ERROR: Failed to read the superblock on %s at %llu\n",
- filename, (unsigned long long)sb_bytenr);
- fprintf(stderr,
- "ERROR: error = '%s', errno = %d\n", strerror(errno), errno);
- return 1;
- }
- printf("superblock: bytenr=%llu, device=%s\n", sb_bytenr, filename);
- printf("---------------------------------------------------------\n");
- if (btrfs_super_magic(sb) != BTRFS_MAGIC && !force) {
- fprintf(stderr,
- "ERROR: bad magic on superblock on %s at %llu\n",
- filename, (unsigned long long)sb_bytenr);
- } else {
- dump_superblock(sb, full);
- }
- return 0;
-}
-
-static int check_csum_sblock(void *sb, int csum_size)
-{
- char result[BTRFS_CSUM_SIZE];
- u32 crc = ~(u32)0;
-
- crc = btrfs_csum_data(NULL, (char *)sb + BTRFS_CSUM_SIZE,
- crc, BTRFS_SUPER_INFO_SIZE - BTRFS_CSUM_SIZE);
- btrfs_csum_final(crc, result);
-
- return !memcmp(sb, &result, csum_size);
-}
-
-static void print_sys_chunk_array(struct btrfs_super_block *sb)
-{
- struct extent_buffer *buf;
- struct btrfs_disk_key *disk_key;
- struct btrfs_chunk *chunk;
- u8 *array_ptr;
- unsigned long sb_array_offset;
- u32 num_stripes;
- u32 array_size;
- u32 len = 0;
- u32 cur_offset;
- struct btrfs_key key;
- int item;
-
- buf = malloc(sizeof(*buf) + sizeof(*sb));
- if (!buf) {
- fprintf(stderr, "%s\n", strerror(ENOMEM));
- exit(1);
- }
- write_extent_buffer(buf, sb, 0, 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);
- cur_offset = 0;
- item = 0;
-
- while (cur_offset < array_size) {
- disk_key = (struct btrfs_disk_key *)array_ptr;
- len = sizeof(*disk_key);
- if (cur_offset + len > array_size)
- goto out_short_read;
-
- btrfs_disk_key_to_cpu(&key, disk_key);
-
- array_ptr += len;
- sb_array_offset += len;
- cur_offset += len;
-
- printf("\titem %d ", item);
- btrfs_print_key(disk_key);
- putchar('\n');
-
- if (key.type == BTRFS_CHUNK_ITEM_KEY) {
- chunk = (struct btrfs_chunk *)sb_array_offset;
- /*
- * At least one btrfs_chunk with one stripe must be
- * present, exact stripe count check comes afterwards
- */
- len = btrfs_chunk_item_size(1);
- if (cur_offset + len > array_size)
- goto out_short_read;
-
- print_chunk(buf, chunk);
- num_stripes = btrfs_chunk_num_stripes(buf, chunk);
- if (!num_stripes) {
- printk(
- "ERROR: invalid number of stripes %u in sys_array at offset %u\n",
- num_stripes, cur_offset);
- break;
- }
- len = btrfs_chunk_item_size(num_stripes);
- if (cur_offset + len > array_size)
- goto out_short_read;
- } else {
- printk(
- "ERROR: unexpected item type %u in sys_array at offset %u\n",
- (u32)key.type, cur_offset);
- break;
- }
- array_ptr += len;
- sb_array_offset += len;
- cur_offset += len;
-
- item++;
- }
-
- free(buf);
- return;
-
-out_short_read:
- printk("ERROR: sys_array too short to read %u bytes at offset %u\n",
- len, cur_offset);
- free(buf);
-}
-
-static int empty_backup(struct btrfs_root_backup *backup)
-{
- if (backup == NULL ||
- (backup->tree_root == 0 &&
- backup->tree_root_gen == 0))
- return 1;
- return 0;
-}
-
-static void print_root_backup(struct btrfs_root_backup *backup)
-{
- printf("\t\tbackup_tree_root:\t%llu\tgen: %llu\tlevel: %d\n",
- btrfs_backup_tree_root(backup),
- btrfs_backup_tree_root_gen(backup),
- btrfs_backup_tree_root_level(backup));
- printf("\t\tbackup_chunk_root:\t%llu\tgen: %llu\tlevel: %d\n",
- btrfs_backup_chunk_root(backup),
- btrfs_backup_chunk_root_gen(backup),
- btrfs_backup_chunk_root_level(backup));
- printf("\t\tbackup_extent_root:\t%llu\tgen: %llu\tlevel: %d\n",
- btrfs_backup_extent_root(backup),
- btrfs_backup_extent_root_gen(backup),
- btrfs_backup_extent_root_level(backup));
- printf("\t\tbackup_fs_root:\t\t%llu\tgen: %llu\tlevel: %d\n",
- btrfs_backup_fs_root(backup),
- btrfs_backup_fs_root_gen(backup),
- btrfs_backup_fs_root_level(backup));
- printf("\t\tbackup_dev_root:\t%llu\tgen: %llu\tlevel: %d\n",
- btrfs_backup_dev_root(backup),
- btrfs_backup_dev_root_gen(backup),
- btrfs_backup_dev_root_level(backup));
- printf("\t\tbackup_csum_root:\t%llu\tgen: %llu\tlevel: %d\n",
- btrfs_backup_csum_root(backup),
- btrfs_backup_csum_root_gen(backup),
- btrfs_backup_csum_root_level(backup));
-
- printf("\t\tbackup_total_bytes:\t%llu\n",
- btrfs_backup_total_bytes(backup));
- printf("\t\tbackup_bytes_used:\t%llu\n",
- btrfs_backup_bytes_used(backup));
- printf("\t\tbackup_num_devices:\t%llu\n",
- btrfs_backup_num_devices(backup));
- putchar('\n');
-}
-
-static void print_backup_roots(struct btrfs_super_block *sb)
-{
- struct btrfs_root_backup *backup;
- int i;
-
- for (i = 0; i < BTRFS_NUM_BACKUP_ROOTS; i++) {
- backup = sb->super_roots + i;
- if (!empty_backup(backup)) {
- printf("\tbackup %d:\n", i);
- print_root_backup(backup);
- }
- }
-}
-
-struct readable_flag_entry {
- u64 bit;
- char *output;
-};
-
-#define DEF_INCOMPAT_FLAG_ENTRY(bit_name) \
- {BTRFS_FEATURE_INCOMPAT_##bit_name, #bit_name}
-
-static struct readable_flag_entry incompat_flags_array[] = {
- DEF_INCOMPAT_FLAG_ENTRY(MIXED_BACKREF),
- DEF_INCOMPAT_FLAG_ENTRY(DEFAULT_SUBVOL),
- DEF_INCOMPAT_FLAG_ENTRY(MIXED_GROUPS),
- DEF_INCOMPAT_FLAG_ENTRY(COMPRESS_LZO),
- DEF_INCOMPAT_FLAG_ENTRY(COMPRESS_LZOv2),
- DEF_INCOMPAT_FLAG_ENTRY(BIG_METADATA),
- DEF_INCOMPAT_FLAG_ENTRY(EXTENDED_IREF),
- DEF_INCOMPAT_FLAG_ENTRY(RAID56),
- DEF_INCOMPAT_FLAG_ENTRY(SKINNY_METADATA),
- DEF_INCOMPAT_FLAG_ENTRY(NO_HOLES)
-};
-static const int incompat_flags_num = sizeof(incompat_flags_array) /
- sizeof(struct readable_flag_entry);
-
-#define DEF_HEADER_FLAG_ENTRY(bit_name) \
- {BTRFS_HEADER_FLAG_##bit_name, #bit_name}
-#define DEF_SUPER_FLAG_ENTRY(bit_name) \
- {BTRFS_SUPER_FLAG_##bit_name, #bit_name}
-
-static struct readable_flag_entry super_flags_array[] = {
- DEF_HEADER_FLAG_ENTRY(WRITTEN),
- DEF_HEADER_FLAG_ENTRY(RELOC),
- DEF_SUPER_FLAG_ENTRY(CHANGING_FSID),
- DEF_SUPER_FLAG_ENTRY(SEEDING),
- DEF_SUPER_FLAG_ENTRY(METADUMP),
- DEF_SUPER_FLAG_ENTRY(METADUMP_V2)
-};
-static const int super_flags_num = ARRAY_SIZE(super_flags_array);
-
-#define BTRFS_SUPER_FLAG_SUPP (BTRFS_HEADER_FLAG_WRITTEN |\
- BTRFS_HEADER_FLAG_RELOC |\
- BTRFS_SUPER_FLAG_CHANGING_FSID |\
- BTRFS_SUPER_FLAG_SEEDING |\
- BTRFS_SUPER_FLAG_METADUMP |\
- BTRFS_SUPER_FLAG_METADUMP_V2)
-
-static void __print_readable_flag(u64 flag, struct readable_flag_entry *array,
- int array_size, u64 supported_flags)
-{
- int i;
- int first = 1;
- struct readable_flag_entry *entry;
-
- if (!flag)
- return;
-
- printf("\t\t\t( ");
- for (i = 0; i < array_size; i++) {
- entry = array + i;
- if (flag & entry->bit) {
- if (first)
- printf("%s ", entry->output);
- else
- printf("|\n\t\t\t %s ", entry->output);
- first = 0;
- }
- }
- flag &= ~supported_flags;
- if (flag) {
- if (first)
- printf("unknown flag: 0x%llx ", flag);
- else
- printf("|\n\t\t\t unknown flag: 0x%llx ", flag);
- }
- printf(")\n");
-}
-
-static void print_readable_incompat_flag(u64 flag)
-{
- return __print_readable_flag(flag, incompat_flags_array,
- incompat_flags_num,
- BTRFS_FEATURE_INCOMPAT_SUPP);
-}
-
-static void print_readable_super_flag(u64 flag)
-{
- return __print_readable_flag(flag, super_flags_array,
- super_flags_num, BTRFS_SUPER_FLAG_SUPP);
-}
-
-static void dump_superblock(struct btrfs_super_block *sb, int full)
-{
- int i;
- char *s, buf[BTRFS_UUID_UNPARSED_SIZE];
- u8 *p;
-
- printf("csum\t\t\t0x");
- for (i = 0, p = sb->csum; i < btrfs_super_csum_size(sb); i++)
- printf("%02x", p[i]);
- if (check_csum_sblock(sb, btrfs_super_csum_size(sb)))
- printf(" [match]");
- else
- printf(" [DON'T MATCH]");
- putchar('\n');
-
- printf("bytenr\t\t\t%llu\n",
- (unsigned long long)btrfs_super_bytenr(sb));
- printf("flags\t\t\t0x%llx\n",
- (unsigned long long)btrfs_super_flags(sb));
- print_readable_super_flag(btrfs_super_flags(sb));
-
- printf("magic\t\t\t");
- s = (char *) &sb->magic;
- for (i = 0; i < 8; i++)
- putchar(isprint(s[i]) ? s[i] : '.');
- if (btrfs_super_magic(sb) == BTRFS_MAGIC)
- printf(" [match]\n");
- else
- printf(" [DON'T MATCH]\n");
-
- uuid_unparse(sb->fsid, buf);
- printf("fsid\t\t\t%s\n", buf);
-
- printf("label\t\t\t");
- s = sb->label;
- for (i = 0; i < BTRFS_LABEL_SIZE && s[i]; i++)
- putchar(isprint(s[i]) ? s[i] : '.');
- putchar('\n');
-
- printf("generation\t\t%llu\n",
- (unsigned long long)btrfs_super_generation(sb));
- printf("root\t\t\t%llu\n", (unsigned long long)btrfs_super_root(sb));
- printf("sys_array_size\t\t%llu\n",
- (unsigned long long)btrfs_super_sys_array_size(sb));
- printf("chunk_root_generation\t%llu\n",
- (unsigned long long)btrfs_super_chunk_root_generation(sb));
- printf("root_level\t\t%llu\n",
- (unsigned long long)btrfs_super_root_level(sb));
- printf("chunk_root\t\t%llu\n",
- (unsigned long long)btrfs_super_chunk_root(sb));
- printf("chunk_root_level\t%llu\n",
- (unsigned long long)btrfs_super_chunk_root_level(sb));
- printf("log_root\t\t%llu\n",
- (unsigned long long)btrfs_super_log_root(sb));
- printf("log_root_transid\t%llu\n",
- (unsigned long long)btrfs_super_log_root_transid(sb));
- printf("log_root_level\t\t%llu\n",
- (unsigned long long)btrfs_super_log_root_level(sb));
- printf("total_bytes\t\t%llu\n",
- (unsigned long long)btrfs_super_total_bytes(sb));
- printf("bytes_used\t\t%llu\n",
- (unsigned long long)btrfs_super_bytes_used(sb));
- printf("sectorsize\t\t%llu\n",
- (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("stripesize\t\t%llu\n",
- (unsigned long long)btrfs_super_stripesize(sb));
- printf("root_dir\t\t%llu\n",
- (unsigned long long)btrfs_super_root_dir(sb));
- printf("num_devices\t\t%llu\n",
- (unsigned long long)btrfs_super_num_devices(sb));
- printf("compat_flags\t\t0x%llx\n",
- (unsigned long long)btrfs_super_compat_flags(sb));
- printf("compat_ro_flags\t\t0x%llx\n",
- (unsigned long long)btrfs_super_compat_ro_flags(sb));
- printf("incompat_flags\t\t0x%llx\n",
- (unsigned long long)btrfs_super_incompat_flags(sb));
- print_readable_incompat_flag(btrfs_super_incompat_flags(sb));
- printf("csum_type\t\t%llu\n",
- (unsigned long long)btrfs_super_csum_type(sb));
- printf("csum_size\t\t%llu\n",
- (unsigned long long)btrfs_super_csum_size(sb));
- printf("cache_generation\t%llu\n",
- (unsigned long long)btrfs_super_cache_generation(sb));
- printf("uuid_tree_generation\t%llu\n",
- (unsigned long long)btrfs_super_uuid_tree_generation(sb));
- uuid_unparse(sb->dev_item.uuid, buf);
- printf("dev_item.uuid\t\t%s\n", buf);
+ if (argc > 1 && !strcmp(argv[1], "--help"))
+ usage(cmd_inspect_dump_super_usage);
- uuid_unparse(sb->dev_item.fsid, buf);
- printf("dev_item.fsid\t\t%s %s\n", buf,
- !memcmp(sb->dev_item.fsid, sb->fsid, BTRFS_FSID_SIZE) ?
- "[match]" : "[DON'T MATCH]");
+ ret = cmd_inspect_dump_super(argc, argv);
- printf("dev_item.type\t\t%llu\n", (unsigned long long)
- btrfs_stack_device_type(&sb->dev_item));
- printf("dev_item.total_bytes\t%llu\n", (unsigned long long)
- btrfs_stack_device_total_bytes(&sb->dev_item));
- printf("dev_item.bytes_used\t%llu\n", (unsigned long long)
- btrfs_stack_device_bytes_used(&sb->dev_item));
- printf("dev_item.io_align\t%u\n", (unsigned int)
- btrfs_stack_device_io_align(&sb->dev_item));
- printf("dev_item.io_width\t%u\n", (unsigned int)
- btrfs_stack_device_io_width(&sb->dev_item));
- printf("dev_item.sector_size\t%u\n", (unsigned int)
- btrfs_stack_device_sector_size(&sb->dev_item));
- printf("dev_item.devid\t\t%llu\n",
- btrfs_stack_device_id(&sb->dev_item));
- printf("dev_item.dev_group\t%u\n", (unsigned int)
- btrfs_stack_device_group(&sb->dev_item));
- printf("dev_item.seek_speed\t%u\n", (unsigned int)
- btrfs_stack_device_seek_speed(&sb->dev_item));
- printf("dev_item.bandwidth\t%u\n", (unsigned int)
- btrfs_stack_device_bandwidth(&sb->dev_item));
- printf("dev_item.generation\t%llu\n", (unsigned long long)
- btrfs_stack_device_generation(&sb->dev_item));
- if (full) {
- printf("sys_chunk_array[%d]:\n", BTRFS_SYSTEM_CHUNK_ARRAY_SIZE);
- print_sys_chunk_array(sb);
- printf("backup_roots[%d]:\n", BTRFS_NUM_BACKUP_ROOTS);
- print_backup_roots(sb);
- }
+ return ret;
}
diff --git a/btrfs-zero-log.c b/btrfs-zero-log.c
index 058e9b19..f5ca9fcf 100644
--- a/btrfs-zero-log.c
+++ b/btrfs-zero-log.c
@@ -31,31 +31,31 @@ __attribute__((noreturn)) static void print_usage(void)
exit(1);
}
-int main(int ac, char **av)
+int main(int argc, char **argv)
{
struct btrfs_root *root;
struct btrfs_trans_handle *trans;
struct btrfs_super_block *sb;
int ret;
- set_argv0(av);
- if (check_argc_exact(ac, 2))
+ set_argv0(argv);
+ if (check_argc_exact(argc - optind, 1))
print_usage();
radix_tree_init();
printf("WARNING: this utility is deprecated, please use 'btrfs rescue zero-log'\n\n");
- if ((ret = check_mounted(av[1])) < 0) {
+ if ((ret = check_mounted(argv[optind])) < 0) {
fprintf(stderr, "ERROR: could not check mount status: %s\n", strerror(-ret));
goto out;
} else if (ret) {
- fprintf(stderr, "ERROR: %s is currently mounted\n", av[1]);
+ fprintf(stderr, "ERROR: %s is currently mounted\n", argv[optind]);
ret = -EBUSY;
goto out;
}
- root = open_ctree(av[1], 0, OPEN_CTREE_WRITES | OPEN_CTREE_PARTIAL);
+ root = open_ctree(argv[optind], 0, OPEN_CTREE_WRITES | OPEN_CTREE_PARTIAL);
if (!root) {
fprintf(stderr, "ERROR: cannot open ctree\n");
return 1;
@@ -63,7 +63,7 @@ int main(int ac, char **av)
sb = root->fs_info->super_copy;
printf("Clearing log on %s, previous log_root %llu, level %u\n",
- av[1],
+ argv[optind],
(unsigned long long)btrfs_super_log_root(sb),
(unsigned)btrfs_super_log_root_level(sb));
trans = btrfs_start_transaction(root, 1);
diff --git a/btrfstune.c b/btrfstune.c
index 0907aa9e..f731e3d2 100644
--- a/btrfstune.c
+++ b/btrfstune.c
@@ -442,9 +442,8 @@ int main(int argc, char *argv[])
}
set_argv0(argv);
- argc = argc - optind;
device = argv[optind];
- if (check_argc_exact(argc, 1)) {
+ if (check_argc_exact(argc - optind, 1)) {
print_usage();
return 1;
}
diff --git a/chunk-recover.c b/chunk-recover.c
index b03330b4..4ad337b4 100644
--- a/chunk-recover.c
+++ b/chunk-recover.c
@@ -47,7 +47,7 @@ struct recover_control {
u16 csum_size;
u32 sectorsize;
- u32 leafsize;
+ u32 nodesize;
u64 generation;
u64 chunk_root_generation;
@@ -477,7 +477,7 @@ static void print_scan_result(struct recover_control *rc)
printf("DEVICE SCAN RESULT:\n");
printf("Filesystem Information:\n");
printf("\tsectorsize: %d\n", rc->sectorsize);
- printf("\tleafsize: %d\n", rc->leafsize);
+ printf("\tnodesize: %d\n", rc->nodesize);
printf("\ttree root generation: %llu\n", rc->generation);
printf("\tchunk root generation: %llu\n", rc->chunk_root_generation);
printf("\n");
@@ -761,10 +761,10 @@ static int scan_one_device(void *dev_scan_struct)
if (ret)
return 1;
- buf = malloc(sizeof(*buf) + rc->leafsize);
+ buf = malloc(sizeof(*buf) + rc->nodesize);
if (!buf)
return -ENOMEM;
- buf->len = rc->leafsize;
+ buf->len = rc->nodesize;
bytenr = 0;
while (1) {
@@ -773,8 +773,8 @@ static int scan_one_device(void *dev_scan_struct)
if (is_super_block_address(bytenr))
bytenr += rc->sectorsize;
- if (pread64(fd, buf->data, rc->leafsize, bytenr) <
- rc->leafsize)
+ if (pread64(fd, buf->data, rc->nodesize, bytenr) <
+ rc->nodesize)
break;
if (memcmp_extent_buffer(buf, rc->fs_devices->fsid,
@@ -818,7 +818,7 @@ static int scan_one_device(void *dev_scan_struct)
break;
}
next_node:
- bytenr += rc->leafsize;
+ bytenr += rc->nodesize;
}
out:
close(fd);
@@ -1070,7 +1070,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->leafsize;
+ old_val += root->nodesize;
else
old_val += key.offset;
btrfs_set_super_bytes_used(fs_info->super_copy,
@@ -1538,7 +1538,7 @@ static int recover_prepare(struct recover_control *rc, char *path)
}
rc->sectorsize = btrfs_super_sectorsize(sb);
- rc->leafsize = btrfs_super_leafsize(sb);
+ rc->nodesize = btrfs_super_nodesize(sb);
rc->generation = btrfs_super_generation(sb);
rc->chunk_root_generation = btrfs_super_chunk_root_generation(sb);
rc->csum_size = btrfs_super_csum_size(sb);
diff --git a/cmds-balance.c b/cmds-balance.c
index e0d10aae..5f05f603 100644
--- a/cmds-balance.c
+++ b/cmds-balance.c
@@ -417,8 +417,13 @@ static int do_balance_v1(int fd)
return ret;
}
+enum {
+ BALANCE_START_FILTERS = 1 << 0,
+ BALANCE_START_NOWARN = 1 << 1
+};
+
static int do_balance(const char *path, struct btrfs_ioctl_balance_args *args,
- int nofilters)
+ unsigned flags)
{
int fd;
int ret;
@@ -429,6 +434,24 @@ static int do_balance(const char *path, struct btrfs_ioctl_balance_args *args,
if (fd < 0)
return 1;
+ if (!(flags & BALANCE_START_FILTERS) && !(flags & BALANCE_START_NOWARN)) {
+ int delay = 10;
+
+ 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 '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");
+ while (delay) {
+ sleep(1);
+ printf("%2d", delay--);
+ fflush(stdout);
+ }
+ printf("\nStarting balance without any filters.\n");
+ }
+
ret = ioctl(fd, BTRFS_IOC_BALANCE_V2, args);
e = errno;
@@ -438,7 +461,7 @@ static int do_balance(const char *path, struct btrfs_ioctl_balance_args *args,
* old one. But, the old one doesn't know any filters, so
* don't fall back if they tried to use the fancy new things
*/
- if (e == ENOTTY && nofilters) {
+ if (e == ENOTTY && !(flags & BALANCE_START_FILTERS)) {
ret = do_balance_v1(fd);
if (ret == 0)
goto out;
@@ -477,13 +500,16 @@ static const char * const cmd_balance_start_usage[] = {
"passed all filters in a comma-separated list of filters for a",
"particular chunk type. If filter list is not given balance all",
"chunks of that type. In case none of the -d, -m or -s options is",
- "given balance all chunks in a filesystem.",
+ "given balance all chunks in a filesystem. This is potentially",
+ "long operation and the user is warned before this start, with",
+ "a delay to stop it.",
"",
"-d[filters] act on data chunks",
"-m[filters] act on metadata chunks",
"-s[filters] act on system chunks (only under -f)",
"-v be verbose",
"-f force reducing of metadata integrity",
+ "--full-balance do not print warning and do not delay start",
NULL
};
@@ -494,19 +520,22 @@ static int cmd_balance_start(int argc, char **argv)
&args.meta, NULL };
int force = 0;
int verbose = 0;
- int nofilters = 1;
+ unsigned start_flags = 0;
int i;
memset(&args, 0, sizeof(args));
optind = 1;
while (1) {
+ enum { GETOPT_VAL_FULL_BALANCE = 256 };
static const struct option longopts[] = {
{ "data", optional_argument, NULL, 'd'},
{ "metadata", optional_argument, NULL, 'm' },
{ "system", optional_argument, NULL, 's' },
{ "force", no_argument, NULL, 'f' },
{ "verbose", no_argument, NULL, 'v' },
+ { "full-balance", no_argument, NULL,
+ GETOPT_VAL_FULL_BALANCE },
{ NULL, 0, NULL, 0 }
};
@@ -516,21 +545,21 @@ static int cmd_balance_start(int argc, char **argv)
switch (opt) {
case 'd':
- nofilters = 0;
+ start_flags |= BALANCE_START_FILTERS;
args.flags |= BTRFS_BALANCE_DATA;
if (parse_filters(optarg, &args.data))
return 1;
break;
case 's':
- nofilters = 0;
+ start_flags |= BALANCE_START_FILTERS;
args.flags |= BTRFS_BALANCE_SYSTEM;
if (parse_filters(optarg, &args.sys))
return 1;
break;
case 'm':
- nofilters = 0;
+ start_flags |= BALANCE_START_FILTERS;
args.flags |= BTRFS_BALANCE_METADATA;
if (parse_filters(optarg, &args.meta))
@@ -542,6 +571,9 @@ static int cmd_balance_start(int argc, char **argv)
case 'v':
verbose = 1;
break;
+ case GETOPT_VAL_FULL_BALANCE:
+ start_flags |= BALANCE_START_NOWARN;
+ break;
default:
usage(cmd_balance_start_usage);
}
@@ -567,7 +599,7 @@ static int cmd_balance_start(int argc, char **argv)
sizeof(struct btrfs_balance_args));
}
- if (nofilters) {
+ if (!(start_flags & BALANCE_START_FILTERS)) {
/* relocate everything - no filters */
args.flags |= BTRFS_BALANCE_TYPE_MASK;
}
@@ -595,7 +627,7 @@ static int cmd_balance_start(int argc, char **argv)
if (verbose)
dump_ioctl_balance_args(&args);
- return do_balance(argv[optind], &args, nofilters);
+ return do_balance(argv[optind], &args, start_flags);
}
static const char * const cmd_balance_pause_usage[] = {
@@ -612,10 +644,12 @@ static int cmd_balance_pause(int argc, char **argv)
int e;
DIR *dirstream = NULL;
- if (check_argc_exact(argc, 2))
+ clean_args_no_options(argc, argv, cmd_balance_pause_usage);
+
+ if (check_argc_exact(argc - optind, 1))
usage(cmd_balance_pause_usage);
- path = argv[1];
+ path = argv[optind];
fd = btrfs_open_dir(path, &dirstream, 1);
if (fd < 0)
@@ -651,10 +685,12 @@ static int cmd_balance_cancel(int argc, char **argv)
int e;
DIR *dirstream = NULL;
- if (check_argc_exact(argc, 2))
+ clean_args_no_options(argc, argv, cmd_balance_cancel_usage);
+
+ if (check_argc_exact(argc - optind, 1))
usage(cmd_balance_cancel_usage);
- path = argv[1];
+ path = argv[optind];
fd = btrfs_open_dir(path, &dirstream, 1);
if (fd < 0)
@@ -691,10 +727,12 @@ static int cmd_balance_resume(int argc, char **argv)
int ret;
int e;
- if (check_argc_exact(argc, 2))
+ clean_args_no_options(argc, argv, cmd_balance_resume_usage);
+
+ if (check_argc_exact(argc - optind, 1))
usage(cmd_balance_resume_usage);
- path = argv[1];
+ path = argv[optind];
fd = btrfs_open_dir(path, &dirstream, 1);
if (fd < 0)
@@ -827,6 +865,16 @@ static int cmd_balance_status(int argc, char **argv)
return 1;
}
+static int cmd_balance_full(int argc, char **argv)
+{
+ struct btrfs_ioctl_balance_args args;
+
+ memset(&args, 0, sizeof(args));
+ args.flags |= BTRFS_BALANCE_TYPE_MASK;
+
+ return do_balance(argv[1], &args, BALANCE_START_NOWARN);
+}
+
static const char balance_cmd_group_info[] =
"balance data accross devices, or change block groups using filters";
@@ -837,20 +885,21 @@ const struct cmd_group balance_cmd_group = {
{ "cancel", cmd_balance_cancel, cmd_balance_cancel_usage, NULL, 0 },
{ "resume", cmd_balance_resume, cmd_balance_resume_usage, NULL, 0 },
{ "status", cmd_balance_status, cmd_balance_status_usage, NULL, 0 },
+ { "--full-balance", cmd_balance_full, NULL, NULL, 1 },
NULL_CMD_STRUCT
}
};
int cmd_balance(int argc, char **argv)
{
- if (argc == 2) {
+ if (argc == 2 && strcmp("start", argv[1]) != 0) {
/* old 'btrfs filesystem balance <path>' syntax */
struct btrfs_ioctl_balance_args args;
memset(&args, 0, sizeof(args));
args.flags |= BTRFS_BALANCE_TYPE_MASK;
- return do_balance(argv[1], &args, 1);
+ return do_balance(argv[1], &args, 0);
}
return handle_command_group(&balance_cmd_group, argc, argv);
diff --git a/cmds-check.c b/cmds-check.c
index 0165fba4..127ac977 100644
--- a/cmds-check.c
+++ b/cmds-check.c
@@ -73,40 +73,7 @@ static int init_extent_tree = 0;
static int check_data_csum = 0;
static struct btrfs_fs_info *global_info;
static struct task_ctx ctx = { 0 };
-
-static void *print_status_check(void *p)
-{
- struct task_ctx *priv = p;
- const char work_indicator[] = { '.', 'o', 'O', 'o' };
- uint32_t count = 0;
- static char *task_position_string[] = {
- "checking extents",
- "checking free space cache",
- "checking fs roots",
- };
-
- task_period_start(priv->info, 1000 /* 1s */);
-
- if (priv->tp == TASK_NOTHING)
- return NULL;
-
- while (1) {
- printf("%s [%c]\r", task_position_string[priv->tp],
- work_indicator[count % 4]);
- count++;
- fflush(stdout);
- task_period_wait(priv->info);
- }
- return NULL;
-}
-
-static int print_status_return(void *p)
-{
- printf("\n");
- fflush(stdout);
-
- return 0;
-}
+static struct cache_tree *roots_info_cache = NULL;
struct extent_backref {
struct list_head list;
@@ -227,6 +194,156 @@ struct file_extent_hole {
u64 len;
};
+struct inode_record {
+ struct list_head backrefs;
+ unsigned int checked:1;
+ unsigned int merging:1;
+ unsigned int found_inode_item:1;
+ unsigned int found_dir_item:1;
+ unsigned int found_file_extent:1;
+ unsigned int found_csum_item:1;
+ unsigned int some_csum_missing:1;
+ unsigned int nodatasum:1;
+ int errors;
+
+ u64 ino;
+ u32 nlink;
+ u32 imode;
+ u64 isize;
+ u64 nbytes;
+
+ u32 found_link;
+ u64 found_size;
+ u64 extent_start;
+ u64 extent_end;
+ struct rb_root holes;
+ struct list_head orphan_extents;
+
+ u32 refs;
+};
+
+#define I_ERR_NO_INODE_ITEM (1 << 0)
+#define I_ERR_NO_ORPHAN_ITEM (1 << 1)
+#define I_ERR_DUP_INODE_ITEM (1 << 2)
+#define I_ERR_DUP_DIR_INDEX (1 << 3)
+#define I_ERR_ODD_DIR_ITEM (1 << 4)
+#define I_ERR_ODD_FILE_EXTENT (1 << 5)
+#define I_ERR_BAD_FILE_EXTENT (1 << 6)
+#define I_ERR_FILE_EXTENT_OVERLAP (1 << 7)
+#define I_ERR_FILE_EXTENT_DISCOUNT (1 << 8) // 100
+#define I_ERR_DIR_ISIZE_WRONG (1 << 9)
+#define I_ERR_FILE_NBYTES_WRONG (1 << 10) // 400
+#define I_ERR_ODD_CSUM_ITEM (1 << 11)
+#define I_ERR_SOME_CSUM_MISSING (1 << 12)
+#define I_ERR_LINK_COUNT_WRONG (1 << 13)
+#define I_ERR_FILE_EXTENT_ORPHAN (1 << 14)
+
+struct root_backref {
+ struct list_head list;
+ unsigned int found_dir_item:1;
+ unsigned int found_dir_index:1;
+ unsigned int found_back_ref:1;
+ unsigned int found_forward_ref:1;
+ unsigned int reachable:1;
+ int errors;
+ u64 ref_root;
+ u64 dir;
+ u64 index;
+ u16 namelen;
+ char name[0];
+};
+
+struct root_record {
+ struct list_head backrefs;
+ struct cache_extent cache;
+ unsigned int found_root_item:1;
+ u64 objectid;
+ u32 found_ref;
+};
+
+struct ptr_node {
+ struct cache_extent cache;
+ void *data;
+};
+
+struct shared_node {
+ struct cache_extent cache;
+ struct cache_tree root_cache;
+ struct cache_tree inode_cache;
+ struct inode_record *current;
+ u32 refs;
+};
+
+struct block_info {
+ u64 start;
+ u32 size;
+};
+
+struct walk_control {
+ struct cache_tree shared;
+ struct shared_node *nodes[BTRFS_MAX_LEVEL];
+ int active_node;
+ int root_level;
+};
+
+struct bad_item {
+ struct btrfs_key key;
+ u64 root_id;
+ struct list_head list;
+};
+
+struct extent_entry {
+ u64 bytenr;
+ u64 bytes;
+ int count;
+ int broken;
+ struct list_head list;
+};
+
+struct root_item_info {
+ /* level of the root */
+ u8 level;
+ /* number of nodes at this level, must be 1 for a root */
+ int node_count;
+ u64 bytenr;
+ u64 gen;
+ struct cache_extent cache_extent;
+};
+
+static void *print_status_check(void *p)
+{
+ struct task_ctx *priv = p;
+ const char work_indicator[] = { '.', 'o', 'O', 'o' };
+ uint32_t count = 0;
+ static char *task_position_string[] = {
+ "checking extents",
+ "checking free space cache",
+ "checking fs roots",
+ };
+
+ task_period_start(priv->info, 1000 /* 1s */);
+
+ if (priv->tp == TASK_NOTHING)
+ return NULL;
+
+ while (1) {
+ printf("%s [%c]\r", task_position_string[priv->tp],
+ work_indicator[count % 4]);
+ count++;
+ fflush(stdout);
+ task_period_wait(priv->info);
+ }
+ return NULL;
+}
+
+static int print_status_return(void *p)
+{
+ printf("\n");
+ fflush(stdout);
+
+ return 0;
+}
+
/* Compatible function to allow reuse of old codes */
static u64 first_extent_gap(struct rb_root *holes)
{
@@ -419,104 +536,6 @@ static void free_file_extent_holes(struct rb_root *holes)
}
}
-struct inode_record {
- struct list_head backrefs;
- unsigned int checked:1;
- unsigned int merging:1;
- unsigned int found_inode_item:1;
- unsigned int found_dir_item:1;
- unsigned int found_file_extent:1;
- unsigned int found_csum_item:1;
- unsigned int some_csum_missing:1;
- unsigned int nodatasum:1;
- int errors;
-
- u64 ino;
- u32 nlink;
- u32 imode;
- u64 isize;
- u64 nbytes;
-
- u32 found_link;
- u64 found_size;
- u64 extent_start;
- u64 extent_end;
- struct rb_root holes;
- struct list_head orphan_extents;
-
- u32 refs;
-};
-
-#define I_ERR_NO_INODE_ITEM (1 << 0)
-#define I_ERR_NO_ORPHAN_ITEM (1 << 1)
-#define I_ERR_DUP_INODE_ITEM (1 << 2)
-#define I_ERR_DUP_DIR_INDEX (1 << 3)
-#define I_ERR_ODD_DIR_ITEM (1 << 4)
-#define I_ERR_ODD_FILE_EXTENT (1 << 5)
-#define I_ERR_BAD_FILE_EXTENT (1 << 6)
-#define I_ERR_FILE_EXTENT_OVERLAP (1 << 7)
-#define I_ERR_FILE_EXTENT_DISCOUNT (1 << 8) // 100
-#define I_ERR_DIR_ISIZE_WRONG (1 << 9)
-#define I_ERR_FILE_NBYTES_WRONG (1 << 10) // 400
-#define I_ERR_ODD_CSUM_ITEM (1 << 11)
-#define I_ERR_SOME_CSUM_MISSING (1 << 12)
-#define I_ERR_LINK_COUNT_WRONG (1 << 13)
-#define I_ERR_FILE_EXTENT_ORPHAN (1 << 14)
-
-struct root_backref {
- struct list_head list;
- unsigned int found_dir_item:1;
- unsigned int found_dir_index:1;
- unsigned int found_back_ref:1;
- unsigned int found_forward_ref:1;
- unsigned int reachable:1;
- int errors;
- u64 ref_root;
- u64 dir;
- u64 index;
- u16 namelen;
- char name[0];
-};
-
-struct root_record {
- struct list_head backrefs;
- struct cache_extent cache;
- unsigned int found_root_item:1;
- u64 objectid;
- u32 found_ref;
-};
-
-struct ptr_node {
- struct cache_extent cache;
- void *data;
-};
-
-struct shared_node {
- struct cache_extent cache;
- struct cache_tree root_cache;
- struct cache_tree inode_cache;
- struct inode_record *current;
- u32 refs;
-};
-
-struct block_info {
- u64 start;
- u32 size;
-};
-
-struct walk_control {
- struct cache_tree shared;
- struct shared_node *nodes[BTRFS_MAX_LEVEL];
- int active_node;
- int root_level;
-};
-
-struct bad_item {
- struct btrfs_key key;
- u64 root_id;
- struct list_head list;
-};
-
static void reset_cached_block_groups(struct btrfs_fs_info *fs_info);
static void record_root_in_trans(struct btrfs_trans_handle *trans,
@@ -1753,7 +1772,7 @@ static void reada_walk_down(struct btrfs_root *root,
return;
nritems = btrfs_header_nritems(node);
- blocksize = btrfs_level_size(root, level - 1);
+ blocksize = root->nodesize;
for (i = slot; i < nritems; i++) {
bytenr = btrfs_node_blockptr(node, i);
ptr_gen = btrfs_node_ptr_generation(node, i);
@@ -1860,7 +1879,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 = btrfs_level_size(root, *level - 1);
+ blocksize = root->nodesize;
ret = btrfs_lookup_extent_info(NULL, root, bytenr, *level - 1,
1, &refs, NULL);
if (ret < 0)
@@ -1890,7 +1909,7 @@ 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->leafsize, *level);
+ root->nodesize, *level);
err = -EIO;
goto out;
}
@@ -4490,6 +4509,76 @@ static void check_extent_type(struct extent_record *rec)
}
}
+static int add_extent_rec_nolookup(struct cache_tree *extent_cache,
+ struct btrfs_key *parent_key, u64 parent_gen,
+ u64 start, u64 nr, u64 extent_item_refs,
+ int is_root, int inc_ref, int set_checked,
+ int metadata, int extent_rec, u64 max_size)
+{
+ struct extent_record *rec;
+ int ret = 0;
+
+ rec = malloc(sizeof(*rec));
+ if (!rec)
+ return -ENOMEM;
+ rec->start = start;
+ rec->max_size = max_size;
+ rec->nr = max(nr, max_size);
+ rec->found_rec = !!extent_rec;
+ rec->content_checked = 0;
+ rec->owner_ref_checked = 0;
+ rec->num_duplicates = 0;
+ rec->metadata = metadata;
+ rec->flag_block_full_backref = -1;
+ rec->bad_full_backref = 0;
+ rec->crossing_stripes = 0;
+ rec->wrong_chunk_type = 0;
+ INIT_LIST_HEAD(&rec->backrefs);
+ INIT_LIST_HEAD(&rec->dups);
+ INIT_LIST_HEAD(&rec->list);
+
+ if (is_root)
+ rec->is_root = 1;
+ else
+ rec->is_root = 0;
+
+ if (inc_ref)
+ rec->refs = 1;
+ else
+ rec->refs = 0;
+
+ if (extent_item_refs)
+ rec->extent_item_refs = extent_item_refs;
+ else
+ rec->extent_item_refs = 0;
+
+ if (parent_key)
+ btrfs_cpu_key_to_disk(&rec->parent_key, parent_key);
+ else
+ memset(&rec->parent_key, 0, sizeof(*parent_key));
+
+ if (parent_gen)
+ rec->parent_generation = parent_gen;
+ else
+ rec->parent_generation = 0;
+
+ rec->cache.start = start;
+ rec->cache.size = nr;
+ ret = insert_cache_extent(extent_cache, &rec->cache);
+ BUG_ON(ret);
+ bytes_used += nr;
+ if (set_checked) {
+ rec->content_checked = 1;
+ rec->owner_ref_checked = 1;
+ }
+
+ if (metadata)
+ rec->crossing_stripes = check_crossing_stripes(rec->start,
+ rec->max_size);
+ check_extent_type(rec);
+ return ret;
+}
+
static int add_extent_rec(struct cache_tree *extent_cache,
struct btrfs_key *parent_key, u64 parent_gen,
u64 start, u64 nr, u64 extent_item_refs,
@@ -4579,71 +4668,18 @@ static int add_extent_rec(struct cache_tree *extent_cache,
* As now stripe_len is fixed to BTRFS_STRIPE_LEN, just check
* it.
*/
- if (metadata && check_crossing_stripes(rec->start,
- rec->max_size))
- rec->crossing_stripes = 1;
+ if (metadata)
+ rec->crossing_stripes = check_crossing_stripes(
+ rec->start, rec->max_size);
check_extent_type(rec);
maybe_free_extent_rec(extent_cache, rec);
return ret;
}
- rec = malloc(sizeof(*rec));
- if (!rec)
- return -ENOMEM;
- rec->start = start;
- rec->max_size = max_size;
- rec->nr = max(nr, max_size);
- rec->found_rec = !!extent_rec;
- rec->content_checked = 0;
- rec->owner_ref_checked = 0;
- rec->num_duplicates = 0;
- rec->metadata = metadata;
- rec->flag_block_full_backref = -1;
- rec->bad_full_backref = 0;
- rec->crossing_stripes = 0;
- rec->wrong_chunk_type = 0;
- INIT_LIST_HEAD(&rec->backrefs);
- INIT_LIST_HEAD(&rec->dups);
- INIT_LIST_HEAD(&rec->list);
-
- if (is_root)
- rec->is_root = 1;
- else
- rec->is_root = 0;
-
- if (inc_ref)
- rec->refs = 1;
- else
- rec->refs = 0;
-
- if (extent_item_refs)
- rec->extent_item_refs = extent_item_refs;
- else
- rec->extent_item_refs = 0;
- if (parent_key)
- btrfs_cpu_key_to_disk(&rec->parent_key, parent_key);
- else
- memset(&rec->parent_key, 0, sizeof(*parent_key));
+ ret = add_extent_rec_nolookup(extent_cache, parent_key, parent_gen,
+ start, nr, extent_item_refs, is_root, inc_ref,
+ set_checked, metadata, extent_rec, max_size);
- if (parent_gen)
- rec->parent_generation = parent_gen;
- else
- rec->parent_generation = 0;
-
- rec->cache.start = start;
- rec->cache.size = nr;
- ret = insert_cache_extent(extent_cache, &rec->cache);
- BUG_ON(ret);
- bytes_used += nr;
- if (set_checked) {
- rec->content_checked = 1;
- rec->owner_ref_checked = 1;
- }
-
- if (metadata)
- if (check_crossing_stripes(rec->start, rec->max_size))
- rec->crossing_stripes = 1;
- check_extent_type(rec);
return ret;
}
@@ -4656,7 +4692,7 @@ static int add_tree_backref(struct cache_tree *extent_cache, u64 bytenr,
cache = lookup_cache_extent(extent_cache, bytenr, 1);
if (!cache) {
- add_extent_rec(extent_cache, NULL, 0, bytenr,
+ add_extent_rec_nolookup(extent_cache, NULL, 0, bytenr,
1, 0, 0, 0, 0, 1, 0, 0);
cache = lookup_cache_extent(extent_cache, bytenr, 1);
if (!cache)
@@ -4708,8 +4744,8 @@ static int add_data_backref(struct cache_tree *extent_cache, u64 bytenr,
cache = lookup_cache_extent(extent_cache, bytenr, 1);
if (!cache) {
- add_extent_rec(extent_cache, NULL, 0, bytenr, 1, 0, 0, 0, 0,
- 0, 0, max_size);
+ add_extent_rec_nolookup(extent_cache, NULL, 0, bytenr, 1, 0, 0,
+ 0, 0, 0, 0, max_size);
cache = lookup_cache_extent(extent_cache, bytenr, 1);
if (!cache)
abort();
@@ -5187,7 +5223,7 @@ static int process_extent_item(struct btrfs_root *root,
if (key.type == BTRFS_METADATA_ITEM_KEY) {
metadata = 1;
- num_bytes = root->leafsize;
+ num_bytes = root->nodesize;
} else {
num_bytes = key.offset;
}
@@ -5407,7 +5443,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->leafsize;
+ last = key.objectid + root->nodesize;
path->slots[0]++;
continue;
}
@@ -5419,7 +5455,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->leafsize;
+ last = key.objectid + root->nodesize;
path->slots[0]++;
}
@@ -6178,7 +6214,7 @@ static int run_next_block(struct btrfs_root *root,
level = btrfs_header_level(buf);
for (i = 0; i < nritems; i++) {
ptr = btrfs_node_blockptr(buf, i);
- size = btrfs_level_size(root, level - 1);
+ size = root->nodesize;
btrfs_node_key_to_cpu(buf, &key, i);
if (ri != NULL) {
if ((level == ri->drop_level)
@@ -6379,7 +6415,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->leafsize;
+ found_key.offset : root->nodesize;
ret = btrfs_update_block_group(trans, root, bytenr,
bytes, 0, 0);
@@ -6414,7 +6450,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->leafsize);
+ info->extent_root->nodesize);
if (!allocated) {
u32 item_size = sizeof(*ei);
@@ -6529,14 +6565,6 @@ fail:
return ret;
}
-struct extent_entry {
- u64 bytenr;
- u64 bytes;
- int count;
- int broken;
- struct list_head list;
-};
-
static struct extent_entry *find_entry(struct list_head *entries,
u64 bytenr, u64 bytes)
{
@@ -8201,14 +8229,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,
- btrfs_level_size(root1, level), NULL);
+ root1->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,
- btrfs_level_size(root1, level), NULL);
+ root1->nodesize, NULL);
if (ret < 0)
goto out;
btrfs_init_path(&path);
@@ -8239,7 +8267,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 = btrfs_level_size(root, level);
+ level_size = root->nodesize;
ret = add_root_item_to_list(&normal_trees,
found_key.objectid,
btrfs_root_bytenr(&ri),
@@ -8249,7 +8277,7 @@ again:
goto out;
} else {
level = btrfs_root_level(&ri);
- level_size = btrfs_level_size(root, level);
+ level_size = root->nodesize;
objectid = found_key.objectid;
btrfs_disk_key_to_cpu(&found_key,
&ri.drop_progress);
@@ -8364,7 +8392,7 @@ static int btrfs_fsck_reinit_root(struct btrfs_trans_handle *trans,
goto init;
}
c = btrfs_alloc_free_block(trans, root,
- btrfs_level_size(root, 0),
+ root->nodesize,
root->root_key.objectid,
&disk_key, level, 0, 0);
if (IS_ERR(c)) {
@@ -8421,7 +8449,7 @@ static int pin_down_tree_blocks(struct btrfs_fs_info *fs_info,
struct btrfs_root_item *ri;
struct btrfs_key key;
u64 bytenr;
- u32 leafsize;
+ u32 nodesize;
int level = btrfs_header_level(eb);
int nritems;
int ret;
@@ -8438,7 +8466,7 @@ static int pin_down_tree_blocks(struct btrfs_fs_info *fs_info,
btrfs_pin_extent(fs_info, eb->start, eb->len);
- leafsize = btrfs_super_leafsize(fs_info->super_copy);
+ nodesize = btrfs_super_nodesize(fs_info->super_copy);
nritems = btrfs_header_nritems(eb);
for (i = 0; i < nritems; i++) {
if (level == 0) {
@@ -8460,7 +8488,7 @@ static int pin_down_tree_blocks(struct btrfs_fs_info *fs_info,
* just pass in extent_root.
*/
tmp = read_tree_block(fs_info->extent_root, bytenr,
- leafsize, 0);
+ nodesize, 0);
if (!extent_buffer_uptodate(tmp)) {
fprintf(stderr, "Error reading root block\n");
return -EIO;
@@ -8474,12 +8502,12 @@ static int pin_down_tree_blocks(struct btrfs_fs_info *fs_info,
/* If we aren't the tree root don't read the block */
if (level == 1 && !tree_root) {
- btrfs_pin_extent(fs_info, bytenr, leafsize);
+ btrfs_pin_extent(fs_info, bytenr, nodesize);
continue;
}
tmp = read_tree_block(fs_info->extent_root, bytenr,
- leafsize, 0);
+ nodesize, 0);
if (!extent_buffer_uptodate(tmp)) {
fprintf(stderr, "Error reading tree block\n");
return -EIO;
@@ -9117,18 +9145,6 @@ static int fill_csum_tree(struct btrfs_trans_handle *trans,
return fill_csum_tree_from_extent(trans, csum_root);
}
-struct root_item_info {
- /* level of the root */
- u8 level;
- /* number of nodes at this level, must be 1 for a root */
- int node_count;
- u64 bytenr;
- u64 gen;
- struct cache_extent cache_extent;
-};
-
-static struct cache_tree *roots_info_cache = NULL;
-
static void free_roots_info_cache(void)
{
if (!roots_info_cache)
@@ -9470,7 +9486,7 @@ const char * const cmd_check_usage[] = {
"WARNING: the repair mode is considered dangerous",
"",
"-s|--super <superblock> use this superblock copy",
- "-b|--backup use the backup root copy",
+ "-b|--backup use the first valid backup root copy",
"--repair try to repair the filesystem",
"--readonly run in read-only mode (default)",
"--init-csum-tree create a new CRC tree",
@@ -9480,6 +9496,7 @@ const char * const cmd_check_usage[] = {
"-E|--subvol-extents <subvolid>",
" print subvolume extents and sharing state",
"-r|--tree-root <bytenr> use the given bytenr for the tree root",
+ "--chunk-root <bytenr> use the given bytenr for the chunk tree root",
"-p|--progress indicate progress",
NULL
};
@@ -9492,6 +9509,7 @@ int cmd_check(int argc, char **argv)
u64 bytenr = 0;
u64 subvolid = 0;
u64 tree_root_bytenr = 0;
+ u64 chunk_root_bytenr = 0;
char uuidbuf[BTRFS_UUID_UNPARSED_SIZE];
int ret;
u64 num;
@@ -9502,19 +9520,25 @@ int cmd_check(int argc, char **argv)
while(1) {
int c;
- enum { OPT_REPAIR = 257, OPT_INIT_CSUM, OPT_INIT_EXTENT,
- OPT_CHECK_CSUM, OPT_READONLY };
+ enum { GETOPT_VAL_REPAIR = 257, GETOPT_VAL_INIT_CSUM,
+ GETOPT_VAL_INIT_EXTENT, GETOPT_VAL_CHECK_CSUM,
+ GETOPT_VAL_READONLY, GETOPT_VAL_CHUNK_TREE };
static const struct option long_options[] = {
{ "super", required_argument, NULL, 's' },
- { "repair", no_argument, NULL, OPT_REPAIR },
- { "readonly", no_argument, NULL, OPT_READONLY },
- { "init-csum-tree", no_argument, NULL, OPT_INIT_CSUM },
- { "init-extent-tree", no_argument, NULL, OPT_INIT_EXTENT },
- { "check-data-csum", no_argument, NULL, OPT_CHECK_CSUM },
+ { "repair", no_argument, NULL, GETOPT_VAL_REPAIR },
+ { "readonly", no_argument, NULL, GETOPT_VAL_READONLY },
+ { "init-csum-tree", no_argument, NULL,
+ GETOPT_VAL_INIT_CSUM },
+ { "init-extent-tree", no_argument, NULL,
+ GETOPT_VAL_INIT_EXTENT },
+ { "check-data-csum", no_argument, NULL,
+ GETOPT_VAL_CHECK_CSUM },
{ "backup", no_argument, NULL, 'b' },
{ "subvol-extents", required_argument, NULL, 'E' },
{ "qgroup-report", no_argument, NULL, 'Q' },
{ "tree-root", required_argument, NULL, 'r' },
+ { "chunk-root", required_argument, NULL,
+ GETOPT_VAL_CHUNK_TREE },
{ "progress", no_argument, NULL, 'p' },
{ NULL, 0, NULL, 0}
};
@@ -9548,40 +9572,42 @@ int cmd_check(int argc, char **argv)
case 'r':
tree_root_bytenr = arg_strtou64(optarg);
break;
+ case GETOPT_VAL_CHUNK_TREE:
+ chunk_root_bytenr = arg_strtou64(optarg);
+ break;
case 'p':
ctx.progress_enabled = true;
break;
case '?':
case 'h':
usage(cmd_check_usage);
- case OPT_REPAIR:
+ case GETOPT_VAL_REPAIR:
printf("enabling repair mode\n");
repair = 1;
ctree_flags |= OPEN_CTREE_WRITES;
break;
- case OPT_READONLY:
+ case GETOPT_VAL_READONLY:
readonly = 1;
break;
- case OPT_INIT_CSUM:
+ case GETOPT_VAL_INIT_CSUM:
printf("Creating a new CRC tree\n");
init_csum_tree = 1;
repair = 1;
ctree_flags |= OPEN_CTREE_WRITES;
break;
- case OPT_INIT_EXTENT:
+ case GETOPT_VAL_INIT_EXTENT:
init_extent_tree = 1;
ctree_flags |= (OPEN_CTREE_WRITES |
OPEN_CTREE_NO_BLOCK_GROUPS);
repair = 1;
break;
- case OPT_CHECK_CSUM:
+ case GETOPT_VAL_CHECK_CSUM:
check_data_csum = 1;
break;
}
}
- argc = argc - optind;
- if (check_argc_exact(argc, 1))
+ if (check_argc_exact(argc - optind, 1))
usage(cmd_check_usage);
if (ctx.progress_enabled) {
@@ -9612,7 +9638,7 @@ int cmd_check(int argc, char **argv)
ctree_flags |= OPEN_CTREE_PARTIAL;
info = open_ctree_fs_info(argv[optind], bytenr, tree_root_bytenr,
- ctree_flags);
+ chunk_root_bytenr, ctree_flags);
if (!info) {
fprintf(stderr, "Couldn't open file system\n");
ret = -EIO;
@@ -9645,7 +9671,7 @@ int cmd_check(int argc, char **argv)
uuidbuf);
ret = qgroup_verify_all(info);
if (ret == 0)
- print_qgroup_report(1);
+ ret = report_qgroups(1);
goto close_out;
}
if (subvolid) {
@@ -9806,7 +9832,11 @@ int cmd_check(int argc, char **argv)
ret = 1;
}
out:
- print_qgroup_report(0);
+ /* Don't override original ret */
+ if (ret)
+ report_qgroups(0);
+ else
+ ret = report_qgroups(0);
if (found_old_backref) { /*
* there was a disk format change when mixed
* backref was in testing tree. The old format
diff --git a/cmds-device.c b/cmds-device.c
index 50c1c5df..1c886ad5 100644
--- a/cmds-device.c
+++ b/cmds-device.c
@@ -146,7 +146,9 @@ static int _cmd_device_remove(int argc, char **argv,
int i, fdmnt, ret = 0;
DIR *dirstream = NULL;
- if (check_argc_min(argc, 3))
+ clean_args_no_options(argc, argv, usagestr);
+
+ if (check_argc_min(argc - optind, 2))
usage(usagestr);
mntpnt = argv[argc - 1];
@@ -155,22 +157,48 @@ static int _cmd_device_remove(int argc, char **argv,
if (fdmnt < 0)
return 1;
- for(i=1 ; i < argc - 1; i++ ){
+ for(i = optind; i < argc - 1; i++) {
struct btrfs_ioctl_vol_args arg;
+ struct btrfs_ioctl_vol_args_v2 argv2 = {0};
+ int is_devid = 0;
int res;
- if (is_block_device(argv[i]) != 1 && strcmp(argv[i], "missing")) {
+ if (string_is_numerical(argv[i])) {
+ argv2.devid = arg_strtou64(argv[i]);
+ argv2.flags = BTRFS_DEVICE_SPEC_BY_ID;
+ is_devid = 1;
+ } else if (is_block_device(argv[i]) == 1 ||
+ strcmp(argv[i], "missing") == 0) {
+ strncpy_null(argv2.name, argv[i]);
+ } else {
error("not a block device: %s", argv[i]);
ret++;
continue;
}
- memset(&arg, 0, sizeof(arg));
- strncpy_null(arg.name, argv[i]);
+
/*
* Positive values are from BTRFS_ERROR_DEV_*,
* otherwise it's a generic error, one of errnos
*/
- res = ioctl(fdmnt, BTRFS_IOC_RM_DEV, &arg);
+ res = ioctl(fdmnt, BTRFS_IOC_RM_DEV_V2, &argv2);
+
+ /*
+ * If BTRFS_IOC_RM_DEV_V2 is not supported we get ENOTTY and if
+ * argv2.flags includes a flag which kernel doesn't understand then
+ * we shall get EOPNOTSUPP
+ */
+ if (res < 0 && (errno == ENOTTY || errno == EOPNOTSUPP)) {
+ if (is_devid) {
+ error("device delete by id failed: %s",
+ strerror(errno));
+ ret++;
+ continue;
+ }
+ memset(&arg, 0, sizeof(arg));
+ strncpy_null(arg.name, argv[i]);
+ res = ioctl(fdmnt, BTRFS_IOC_RM_DEV, &arg);
+ }
+
if (res) {
const char *msg;
@@ -178,8 +206,13 @@ static int _cmd_device_remove(int argc, char **argv,
msg = btrfs_err_str(res);
else
msg = strerror(errno);
- error("error removing device '%s': %s",
- argv[i], msg);
+ if (is_devid) {
+ error("error removing devid %llu: %s",
+ (unsigned long long)argv2.devid, msg);
+ } else {
+ error("error removing device '%s': %s",
+ argv[i], msg);
+ }
ret++;
}
}
@@ -189,7 +222,7 @@ static int _cmd_device_remove(int argc, char **argv,
}
static const char * const cmd_device_remove_usage[] = {
- "btrfs device remove <device> [<device>...] <path>",
+ "btrfs device remove <device>|<devid> [<device>|<devid>...] <path>",
"Remove a device from a filesystem",
NULL
};
@@ -200,7 +233,7 @@ static int cmd_device_remove(int argc, char **argv)
}
static const char * const cmd_device_delete_usage[] = {
- "btrfs device delete <device> [<device>...] <path>",
+ "btrfs device delete <device>|<devid> [<device>|<devid>...] <path>",
"Remove a device from a filesystem",
NULL
};
@@ -220,7 +253,7 @@ static const char * const cmd_device_scan_usage[] = {
static int cmd_device_scan(int argc, char **argv)
{
int i;
- int devstart = 1;
+ int devstart;
int all = 0;
int ret = 0;
@@ -243,16 +276,17 @@ static int cmd_device_scan(int argc, char **argv)
usage(cmd_device_scan_usage);
}
}
+ devstart = optind;
- if (all && check_argc_max(argc, 2))
+ if (all && check_argc_max(argc - optind, 1))
usage(cmd_device_scan_usage);
- if (all || argc == 1) {
+ if (all || argc - optind == 0) {
printf("Scanning for Btrfs filesystems\n");
ret = btrfs_scan_lblkid();
error_on(ret, "error %d while scanning", ret);
ret = btrfs_register_all_devices();
- error_on(ret, "error %d while registering devices", ret);
+ error_on(ret, "there are %d errors while registering devices", ret);
goto out;
}
@@ -297,7 +331,9 @@ static int cmd_device_ready(int argc, char **argv)
int ret;
char *path;
- if (check_argc_min(argc, 2))
+ clean_args_no_options(argc, argv, cmd_device_ready_usage);
+
+ if (check_argc_exact(argc - optind, 1))
usage(cmd_device_ready_usage);
fd = open("/dev/btrfs-control", O_RDWR);
@@ -306,10 +342,10 @@ static int cmd_device_ready(int argc, char **argv)
return 1;
}
- path = canonicalize_path(argv[argc - 1]);
+ path = canonicalize_path(argv[optind]);
if (!path) {
error("could not canonicalize pathname '%s': %s",
- argv[argc - 1], strerror(errno));
+ argv[optind], strerror(errno));
ret = 1;
goto out;
}
@@ -368,8 +404,7 @@ static int cmd_device_stats(int argc, char **argv)
}
}
- argc = argc - optind;
- if (check_argc_exact(argc, 1))
+ if (check_argc_exact(argc - optind, 1))
usage(cmd_device_stats_usage);
dev_path = argv[optind];
@@ -412,6 +447,17 @@ static int cmd_device_stats(int argc, char **argv)
canonical_path = canonicalize_path((char *)path);
+ /* No path when device is missing. */
+ if (!canonical_path) {
+ canonical_path = malloc(32);
+ if (!canonical_path) {
+ error("not enough memory for path buffer");
+ goto out;
+ }
+ snprintf(canonical_path, 32,
+ "devid:%llu", args.devid);
+ }
+
if (args.nr_items >= BTRFS_DEV_STAT_WRITE_ERRS + 1)
printf("[%s].write_io_errs %llu\n",
canonical_path,
@@ -493,10 +539,12 @@ static int cmd_device_usage(int argc, char **argv)
unit_mode = get_unit_mode_from_arg(&argc, argv, 1);
- if (check_argc_min(argc, 2) || argv[1][0] == '-')
+ clean_args_no_options(argc, argv, cmd_device_usage_usage);
+
+ if (check_argc_min(argc - optind, 1))
usage(cmd_device_usage_usage);
- for (i = 1; i < argc; i++) {
+ for (i = optind; i < argc; i++) {
int fd;
DIR *dirstream = NULL;
diff --git a/cmds-fi-du.c b/cmds-fi-du.c
new file mode 100644
index 00000000..168fc722
--- /dev/null
+++ b/cmds-fi-du.c
@@ -0,0 +1,580 @@
+/*
+ * 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 <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <getopt.h>
+#include <fcntl.h>
+#include <dirent.h>
+
+#include <sys/ioctl.h>
+#include <linux/fs.h>
+#include <linux/fiemap.h>
+
+#include "utils.h"
+#include "commands.h"
+#include "kerncompat.h"
+#include "rbtree.h"
+
+#include "interval_tree_generic.h"
+
+static int summarize = 0;
+static unsigned unit_mode = UNITS_RAW;
+static char path[PATH_MAX] = { 0, };
+static char *pathp = path;
+static char *path_max = &path[PATH_MAX - 1];
+
+struct shared_extent {
+ struct rb_node rb;
+ u64 start; /* Start of interval */
+ u64 last; /* Last location _in_ interval */
+ u64 __subtree_last;
+};
+
+/*
+ * extent_tree_* functions are defined in the massive interval tree
+ * macro below. This serves to illustrate the api in human-readable
+ * terms.
+ */
+static void
+extent_tree_insert(struct shared_extent *node, struct rb_root *root);
+
+static void
+extent_tree_remove(struct shared_extent *node, struct rb_root *root);
+
+static struct shared_extent *
+extent_tree_iter_first(struct rb_root *root,
+ u64 start, u64 last);
+
+static struct shared_extent *
+extent_tree_iter_next(struct shared_extent *node,
+ u64 start, u64 last);
+
+#define START(node) ((node)->start)
+#define LAST(node) ((node)->last)
+
+INTERVAL_TREE_DEFINE(struct shared_extent, rb,
+ u64, __subtree_last,
+ START, LAST, static, extent_tree)
+
+static int add_shared_extent(u64 start, u64 len, struct rb_root *root)
+{
+ struct shared_extent *sh;
+
+ BUG_ON(len == 0);
+
+ sh = calloc(1, sizeof(*sh));
+ if (!sh)
+ return ENOMEM;
+
+ sh->start = start;
+ sh->last = (start + len - 1);
+
+ extent_tree_insert(sh, root);
+
+ return 0;
+}
+
+static void cleanup_shared_extents(struct rb_root *root)
+{
+ struct shared_extent *s;
+ struct shared_extent *tmp;
+
+ if (!root)
+ return;
+
+ s = extent_tree_iter_first(root, 0, -1ULL);
+ while (s) {
+ tmp = extent_tree_iter_next(s, 0, -1ULL);
+ extent_tree_remove(s, root);
+
+ free(s);
+ s = tmp;
+ }
+}
+
+#define dprintf(...)
+
+/*
+ * Find all extents which overlap 'n', calculate the space
+ * covered by them and remove those nodes from the tree.
+ */
+static u64 count_unique_bytes(struct rb_root *root, struct shared_extent *n)
+{
+ struct shared_extent *tmp;
+ u64 wstart = n->start;
+ u64 wlast = n->last;
+
+ dprintf("Count overlaps:");
+
+ do {
+ /*
+ * Expand our search window based on the lastest
+ * overlapping extent. Doing this will allow us to
+ * find all possible overlaps
+ */
+ if (wstart > n->start)
+ wstart = n->start;
+ if (wlast < n->last)
+ wlast = n->last;
+
+ dprintf(" (%llu, %llu)", n->start, n->last);
+
+ tmp = n;
+ n = extent_tree_iter_next(n, wstart, wlast);
+
+ extent_tree_remove(tmp, root);
+ free(tmp);
+ } while (n);
+
+ dprintf("; wstart: %llu wlast: %llu total: %llu\n", wstart,
+ wlast, wlast - wstart + 1);
+
+ return wlast - wstart + 1;
+}
+
+/*
+ * What we want to do here is get a count of shared bytes within the
+ * set of extents we have collected. Specifcally, we don't want to
+ * count any byte more than once, so just adding them up doesn't
+ * work.
+ *
+ * For each set of overlapping extents we find the lowest start and
+ * highest end. From there we have the actual number of bytes which is
+ * shared across all of the extents in our set. A sum of each sets
+ * extent length is returned.
+ */
+static void count_shared_bytes(struct rb_root *root, u64 *ret_cnt)
+{
+ u64 count = 0;
+ struct shared_extent *s = extent_tree_iter_first(root,
+ 0, -1ULL);
+
+ if (!s)
+ goto out;
+
+ while (s) {
+ /*
+ * Find all extents which overlap 's', calculate the space
+ * covered by them and remove those nodes from the tree.
+ */
+ count += count_unique_bytes(root, s);
+
+ /*
+ * Since count_unique_bytes will be emptying the tree,
+ * we can grab the first node here
+ */
+ s = extent_tree_iter_first(root, 0, -1ULL);
+ }
+
+ BUG_ON(!RB_EMPTY_ROOT(root));
+out:
+ *ret_cnt = count;
+}
+
+/* Track which inodes we've seen for the purposes of hardlink detection. */
+struct seen_inode {
+ struct rb_node i_node;
+ u64 i_ino;
+ u64 i_subvol;
+};
+static struct rb_root seen_inodes = RB_ROOT;
+
+static int cmp_si(struct seen_inode *si, u64 ino, u64 subvol)
+{
+ if (ino < si->i_ino)
+ return -1;
+ else if (ino > si->i_ino)
+ return 1;
+ if (subvol < si->i_subvol)
+ return -1;
+ else if (subvol > si->i_subvol)
+ return 1;
+ return 0;
+}
+
+static int mark_inode_seen(u64 ino, u64 subvol)
+{
+ int cmp;
+ struct rb_node **p = &seen_inodes.rb_node;
+ struct rb_node *parent = NULL;
+ struct seen_inode *si;
+
+ while (*p) {
+ parent = *p;
+
+ si = rb_entry(parent, struct seen_inode, i_node);
+ cmp = cmp_si(si, ino, subvol);
+ if (cmp < 0)
+ p = &(*p)->rb_left;
+ else if (cmp > 0)
+ p = &(*p)->rb_right;
+ else
+ BUG();
+ }
+
+ si = calloc(1, sizeof(*si));
+ if (!si)
+ return ENOMEM;
+
+ si->i_ino = ino;
+ si->i_subvol = subvol;
+
+ rb_link_node(&si->i_node, parent, p);
+ rb_insert_color(&si->i_node, &seen_inodes);
+
+ return 0;
+}
+
+static int inode_seen(u64 ino, u64 subvol)
+{
+ int cmp;
+ struct rb_node *n = seen_inodes.rb_node;
+ struct seen_inode *si;
+
+ while (n) {
+ si = rb_entry(n, struct seen_inode, i_node);
+
+ cmp = cmp_si(si, ino, subvol);
+ if (cmp < 0)
+ n = n->rb_left;
+ else if (cmp > 0)
+ n = n->rb_right;
+ else
+ return EEXIST;
+ }
+ return 0;
+}
+
+static void clear_seen_inodes(void)
+{
+ struct rb_node *n = rb_first(&seen_inodes);
+ struct seen_inode *si;
+
+ while (n) {
+ si = rb_entry(n, struct seen_inode, i_node);
+
+ rb_erase(&si->i_node, &seen_inodes);
+ free(si);
+
+ n = rb_first(&seen_inodes);
+ }
+}
+
+/*
+ * Inline extents are skipped because they do not take data space,
+ * delalloc and unknown are skipped because we do not know how much
+ * space they will use yet.
+ */
+#define SKIP_FLAGS (FIEMAP_EXTENT_UNKNOWN|FIEMAP_EXTENT_DELALLOC|FIEMAP_EXTENT_DATA_INLINE)
+static int du_calc_file_space(int fd, struct rb_root *shared_extents,
+ u64 *ret_total, u64 *ret_shared)
+{
+ char buf[16384];
+ struct fiemap *fiemap = (struct fiemap *)buf;
+ struct fiemap_extent *fm_ext = &fiemap->fm_extents[0];
+ int count = (sizeof(buf) - sizeof(*fiemap)) /
+ sizeof(struct fiemap_extent);
+ unsigned int i, ret;
+ int last = 0;
+ int rc;
+ u64 ext_len;
+ u64 file_total = 0;
+ u64 file_shared = 0;
+ u32 flags;
+
+ memset(fiemap, 0, sizeof(struct fiemap));
+
+ do {
+ fiemap->fm_length = ~0ULL;
+ fiemap->fm_extent_count = count;
+ rc = ioctl(fd, FS_IOC_FIEMAP, (unsigned long) fiemap);
+ if (rc < 0) {
+ ret = errno;
+ goto out;
+ }
+
+ /* If 0 extents are returned, then more ioctls are not needed */
+ if (fiemap->fm_mapped_extents == 0)
+ break;
+
+ for (i = 0; i < fiemap->fm_mapped_extents; i++) {
+ ext_len = fm_ext[i].fe_length;
+ flags = fm_ext[i].fe_flags;
+
+ if (flags & FIEMAP_EXTENT_LAST)
+ last = 1;
+
+ if (flags & SKIP_FLAGS)
+ continue;
+
+ file_total += ext_len;
+ if (flags & FIEMAP_EXTENT_SHARED) {
+ file_shared += ext_len;
+
+ if (shared_extents) {
+ ret = add_shared_extent(fm_ext[i].fe_physical,
+ ext_len,
+ shared_extents);
+ if (ret)
+ goto out;
+ }
+ }
+ }
+
+ fiemap->fm_start = (fm_ext[i - 1].fe_logical +
+ fm_ext[i - 1].fe_length);
+ } while (last == 0);
+
+ *ret_total = file_total;
+ *ret_shared = file_shared;
+
+ ret = 0;
+out:
+ return ret;
+}
+
+struct du_dir_ctxt {
+ u64 bytes_total;
+ u64 bytes_shared;
+ DIR *dirstream;
+ struct rb_root shared_extents;
+};
+#define INIT_DU_DIR_CTXT (struct du_dir_ctxt) { 0ULL, 0ULL, NULL, RB_ROOT }
+
+static int du_add_file(const char *filename, int dirfd,
+ struct rb_root *shared_extents, u64 *ret_total,
+ u64 *ret_shared, int top_level);
+
+static int du_walk_dir(struct du_dir_ctxt *ctxt, struct rb_root *shared_extents)
+{
+ int ret, type;
+ struct dirent *entry;
+ DIR *dirstream = ctxt->dirstream;
+
+ ret = 0;
+ do {
+ u64 tot, shr;
+
+ errno = 0;
+ entry = readdir(dirstream);
+ if (entry) {
+ if (strcmp(entry->d_name, ".") == 0
+ || strcmp(entry->d_name, "..") == 0)
+ continue;
+
+ type = entry->d_type;
+ if (type == DT_REG || type == DT_DIR) {
+ tot = shr = 0;
+
+ ret = du_add_file(entry->d_name,
+ dirfd(dirstream),
+ shared_extents, &tot, &shr,
+ 0);
+ if (ret)
+ break;
+
+ ctxt->bytes_total += tot;
+ ctxt->bytes_shared += shr;
+ }
+ }
+ } while (entry != NULL);
+
+ return ret;
+}
+
+static int du_add_file(const char *filename, int dirfd,
+ struct rb_root *shared_extents, u64 *ret_total,
+ u64 *ret_shared, int top_level)
+{
+ int ret, len = strlen(filename);
+ char *pathtmp;
+ struct stat st;
+ struct du_dir_ctxt dir = INIT_DU_DIR_CTXT;
+ int is_dir = 0;
+ u64 file_total = 0;
+ u64 file_shared = 0;
+ u64 dir_set_shared = 0;
+ u64 subvol;
+ int fd;
+ DIR *dirstream = NULL;
+
+ ret = fstatat(dirfd, filename, &st, 0);
+ if (ret) {
+ ret = errno;
+ return ret;
+ }
+
+ if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode))
+ return 0;
+
+ if (len > (path_max - pathp)) {
+ error("path too long: %s %s", path, filename);
+ return ENAMETOOLONG;
+ }
+
+ pathtmp = pathp;
+ if (pathp == path)
+ ret = sprintf(pathp, "%s", filename);
+ else
+ ret = sprintf(pathp, "/%s", filename);
+ pathp += ret;
+
+ fd = open_file_or_dir3(path, &dirstream, O_RDONLY);
+ if (fd < 0) {
+ ret = fd;
+ goto out;
+ }
+
+ ret = lookup_ino_rootid(fd, &subvol);
+ if (ret)
+ goto out_close;
+
+ if (inode_seen(st.st_ino, subvol))
+ goto out_close;
+
+ ret = mark_inode_seen(st.st_ino, subvol);
+ if (ret)
+ goto out_close;
+
+ if (S_ISREG(st.st_mode)) {
+ ret = du_calc_file_space(fd, shared_extents, &file_total,
+ &file_shared);
+ if (ret)
+ goto out_close;
+ } else if (S_ISDIR(st.st_mode)) {
+ struct rb_root *root = shared_extents;
+
+ /*
+ * We collect shared extents in an rb_root, the top
+ * level caller will not pass a root down, so use the
+ * one on our dir context.
+ */
+ if (top_level)
+ root = &dir.shared_extents;
+
+ is_dir = 1;
+
+ dir.dirstream = dirstream;
+ ret = du_walk_dir(&dir, root);
+ *pathp = '\0';
+ if (ret) {
+ if (top_level)
+ cleanup_shared_extents(root);
+ goto out_close;
+ }
+
+ file_total = dir.bytes_total;
+ file_shared = dir.bytes_shared;
+ if (top_level)
+ count_shared_bytes(root, &dir_set_shared);
+ }
+
+ if (!summarize || top_level) {
+ u64 excl = file_total - file_shared;
+
+ if (top_level) {
+ u64 set_shared = file_shared;
+
+ if (is_dir)
+ set_shared = dir_set_shared;
+
+ printf("%10s %10s %10s %s\n",
+ pretty_size_mode(file_total, unit_mode),
+ pretty_size_mode(excl, unit_mode),
+ pretty_size_mode(set_shared, unit_mode),
+ path);
+ } else {
+ printf("%10s %10s %10s %s\n",
+ pretty_size_mode(file_total, unit_mode),
+ pretty_size_mode(excl, unit_mode),
+ "-", path);
+ }
+ }
+
+ if (ret_total)
+ *ret_total = file_total;
+ if (ret_shared)
+ *ret_shared = file_shared;
+
+out_close:
+ close_file_or_dir(fd, dirstream);
+out:
+ /* reset path to just before this element */
+ pathp = pathtmp;
+
+ return ret;
+}
+
+const char * const cmd_filesystem_du_usage[] = {
+ "btrfs filesystem du [options] <path> [<path>..]",
+ "Summarize disk usage of each file.",
+ "-s|--summarize display only a total for each argument",
+ HELPINFO_UNITS_LONG,
+ NULL
+};
+
+int cmd_filesystem_du(int argc, char **argv)
+{
+ int ret = 0, err = 0;
+ int i;
+
+ unit_mode = get_unit_mode_from_arg(&argc, argv, 1);
+
+ optind = 1;
+ while (1) {
+ static const struct option long_options[] = {
+ { "summarize", no_argument, NULL, 's'},
+ { NULL, 0, NULL, 0 }
+ };
+ int c = getopt_long(argc, argv, "s", long_options, NULL);
+
+ if (c < 0)
+ break;
+ switch (c) {
+ case 's':
+ summarize = 1;
+ break;
+ default:
+ usage(cmd_filesystem_du_usage);
+ }
+ }
+
+ if (check_argc_min(argc - optind, 1))
+ usage(cmd_filesystem_du_usage);
+
+ printf("%10s %10s %10s %s\n", "Total", "Exclusive", "Set shared",
+ "Filename");
+
+ for (i = optind; i < argc; i++) {
+ ret = du_add_file(argv[i], AT_FDCWD, NULL, NULL, NULL, 1);
+ if (ret) {
+ error("cannot check space of '%s': %s", argv[i],
+ strerror(ret));
+ err = 1;
+ }
+
+ /* reset hard-link detection for each argument */
+ clear_seen_inodes();
+ }
+
+ return err;
+}
diff --git a/cmds-fi-du.h b/cmds-fi-du.h
new file mode 100644
index 00000000..86e8f24a
--- /dev/null
+++ b/cmds-fi-du.h
@@ -0,0 +1,23 @@
+/*
+ * 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 __CMDS_FILESYSTEM_DU_H__
+#define __CMDS_FILESYSTEM_DU_H__
+
+extern const char * const cmd_filesystem_du_usage[];
+int cmd_filesystem_du(int argc, char **argv);
+
+#endif /* __CMDS_FILESYSTEM_DU_H__ */
diff --git a/cmds-filesystem.c b/cmds-filesystem.c
index 4c6e856b..e27cb263 100644
--- a/cmds-filesystem.c
+++ b/cmds-filesystem.c
@@ -37,7 +37,7 @@
#include "cmds-fi-usage.h"
#include "list_sort.h"
#include "disk-io.h"
-
+#include "cmds-fi-du.h"
/*
* for btrfs fi show, we maintain a hash of fsids we've already printed.
@@ -58,7 +58,14 @@ static int is_seen_fsid(u8 *fsid)
int slot = hash % SEEN_FSID_HASH_SIZE;
struct seen_fsid *seen = seen_fsid_hash[slot];
- return seen ? 1 : 0;
+ while (seen) {
+ if (memcmp(seen->fsid, fsid, BTRFS_FSID_SIZE) == 0)
+ return 1;
+
+ seen = seen->next;
+ }
+
+ return 0;
}
static int add_seen_fsid(u8 *fsid)
@@ -197,10 +204,12 @@ static int cmd_filesystem_df(int argc, char **argv)
unit_mode = get_unit_mode_from_arg(&argc, argv, 1);
- if (argc != 2 || argv[1][0] == '-')
+ clean_args_no_options(argc, argv, cmd_filesystem_df_usage);
+
+ if (check_argc_exact(argc - optind, 1))
usage(cmd_filesystem_df_usage);
- path = argv[1];
+ path = argv[optind];
fd = btrfs_open_dir(path, &dirstream, 1);
if (fd < 0)
@@ -455,14 +464,14 @@ static int btrfs_scan_kernel(void *search, unsigned unit_mode)
memset(label, 0, sizeof(label));
while ((mnt = getmntent(f)) != NULL) {
+ free(dev_info_arg);
+ dev_info_arg = NULL;
if (strcmp(mnt->mnt_type, "btrfs"))
continue;
ret = get_fs_info(mnt->mnt_dir, &fs_info_arg,
&dev_info_arg);
- if (ret) {
- kfree(dev_info_arg);
+ if (ret)
goto out;
- }
/* skip all fs already shown as mounted fs */
if (is_seen_fsid(fs_info_arg.fsid))
@@ -474,14 +483,11 @@ static int btrfs_scan_kernel(void *search, unsigned unit_mode)
ret = get_label_unmounted(
(const char *)dev_info_arg->path, label);
- if (ret) {
- kfree(dev_info_arg);
+ if (ret)
goto out;
- }
+
if (search && !match_search_item_kernel(fs_info_arg.fsid,
mnt->mnt_dir, label, search)) {
- kfree(dev_info_arg);
- dev_info_arg = NULL;
continue;
}
@@ -495,11 +501,10 @@ static int btrfs_scan_kernel(void *search, unsigned unit_mode)
}
if (fd != -1)
close(fd);
- kfree(dev_info_arg);
- dev_info_arg = NULL;
}
out:
+ free(dev_info_arg);
endmntent(f);
return !found;
}
@@ -718,7 +723,7 @@ static int map_seed_devices(struct list_head *all_uuids)
/*
* open_ctree_* detects seed/sprout mapping
*/
- fs_info = open_ctree_fs_info(device->name, 0, 0,
+ fs_info = open_ctree_fs_info(device->name, 0, 0, 0,
OPEN_CTREE_PARTIAL);
if (!fs_info)
continue;
@@ -918,16 +923,17 @@ static int cmd_filesystem_sync(int argc, char **argv)
char *path;
DIR *dirstream = NULL;
- if (check_argc_exact(argc, 2))
+ clean_args_no_options(argc, argv, cmd_filesystem_sync_usage);
+
+ if (check_argc_exact(argc - optind, 1))
usage(cmd_filesystem_sync_usage);
- path = argv[1];
+ path = argv[optind];
fd = btrfs_open_dir(path, &dirstream, 1);
if (fd < 0)
return 1;
- printf("FSSync '%s'\n", path);
res = ioctl(fd, BTRFS_IOC_SYNC);
e = errno;
close_file_or_dir(fd, dirstream);
@@ -1178,11 +1184,13 @@ static int cmd_filesystem_resize(int argc, char **argv)
DIR *dirstream = NULL;
struct stat st;
- if (check_argc_exact(argc, 3))
+ clean_args_no_options(argc, argv, cmd_filesystem_resize_usage);
+
+ if (check_argc_exact(argc - optind, 2))
usage(cmd_filesystem_resize_usage);
- amount = argv[1];
- path = argv[2];
+ amount = argv[optind];
+ path = argv[optind + 1];
len = strlen(amount);
if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
@@ -1247,16 +1255,19 @@ static const char * const cmd_filesystem_label_usage[] = {
static int cmd_filesystem_label(int argc, char **argv)
{
- if (check_argc_min(argc, 2) || check_argc_max(argc, 3))
+ clean_args_no_options(argc, argv, cmd_filesystem_label_usage);
+
+ if (check_argc_min(argc - optind, 1) ||
+ check_argc_max(argc - optind, 2))
usage(cmd_filesystem_label_usage);
- if (argc > 2) {
- return set_label(argv[1], argv[2]);
+ if (argc - optind > 1) {
+ return set_label(argv[optind], argv[optind + 1]);
} else {
char label[BTRFS_LABEL_SIZE];
int ret;
- ret = get_label(argv[1], label);
+ ret = get_label(argv[optind], label);
if (!ret)
fprintf(stdout, "%s\n", label);
@@ -1270,6 +1281,7 @@ static const char filesystem_cmd_group_info[] =
const struct cmd_group filesystem_cmd_group = {
filesystem_cmd_group_usage, filesystem_cmd_group_info, {
{ "df", cmd_filesystem_df, cmd_filesystem_df_usage, NULL, 0 },
+ { "du", cmd_filesystem_du, cmd_filesystem_du_usage, NULL, 0 },
{ "show", cmd_filesystem_show, cmd_filesystem_show_usage, NULL,
0 },
{ "sync", cmd_filesystem_sync, cmd_filesystem_sync_usage, NULL,
diff --git a/cmds-inspect-dump-super.c b/cmds-inspect-dump-super.c
new file mode 100644
index 00000000..3e09ee8c
--- /dev/null
+++ b/cmds-inspect-dump-super.c
@@ -0,0 +1,544 @@
+/*
+ * 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 <fcntl.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <uuid/uuid.h>
+#include <errno.h>
+#include <getopt.h>
+
+#include "ctree.h"
+#include "disk-io.h"
+#include "print-tree.h"
+#include "transaction.h"
+#include "list.h"
+#include "utils.h"
+#include "commands.h"
+#include "crc32c.h"
+#include "cmds-inspect-dump-super.h"
+
+static int check_csum_sblock(void *sb, int csum_size)
+{
+ char result[BTRFS_CSUM_SIZE];
+ u32 crc = ~(u32)0;
+
+ crc = btrfs_csum_data(NULL, (char *)sb + BTRFS_CSUM_SIZE,
+ crc, BTRFS_SUPER_INFO_SIZE - BTRFS_CSUM_SIZE);
+ btrfs_csum_final(crc, result);
+
+ return !memcmp(sb, &result, csum_size);
+}
+
+static void print_sys_chunk_array(struct btrfs_super_block *sb)
+{
+ struct extent_buffer *buf;
+ struct btrfs_disk_key *disk_key;
+ struct btrfs_chunk *chunk;
+ u8 *array_ptr;
+ unsigned long sb_array_offset;
+ u32 num_stripes;
+ u32 array_size;
+ u32 len = 0;
+ u32 cur_offset;
+ struct btrfs_key key;
+ int item;
+
+ buf = malloc(sizeof(*buf) + sizeof(*sb));
+ if (!buf) {
+ error("not enough memory");
+ goto out;
+ }
+ write_extent_buffer(buf, sb, 0, 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);
+ cur_offset = 0;
+ item = 0;
+
+ while (cur_offset < array_size) {
+ disk_key = (struct btrfs_disk_key *)array_ptr;
+ len = sizeof(*disk_key);
+ if (cur_offset + len > array_size)
+ goto out_short_read;
+
+ btrfs_disk_key_to_cpu(&key, disk_key);
+
+ array_ptr += len;
+ sb_array_offset += len;
+ cur_offset += len;
+
+ printf("\titem %d ", item);
+ btrfs_print_key(disk_key);
+ putchar('\n');
+
+ if (key.type == BTRFS_CHUNK_ITEM_KEY) {
+ chunk = (struct btrfs_chunk *)sb_array_offset;
+ /*
+ * At least one btrfs_chunk with one stripe must be
+ * present, exact stripe count check comes afterwards
+ */
+ len = btrfs_chunk_item_size(1);
+ if (cur_offset + len > array_size)
+ goto out_short_read;
+
+ print_chunk(buf, chunk);
+ num_stripes = btrfs_chunk_num_stripes(buf, chunk);
+ if (!num_stripes) {
+ printk(
+ "ERROR: invalid number of stripes %u in sys_array at offset %u\n",
+ num_stripes, cur_offset);
+ break;
+ }
+ len = btrfs_chunk_item_size(num_stripes);
+ if (cur_offset + len > array_size)
+ goto out_short_read;
+ } else {
+ printk(
+ "ERROR: unexpected item type %u in sys_array at offset %u\n",
+ (u32)key.type, cur_offset);
+ break;
+ }
+ array_ptr += len;
+ sb_array_offset += len;
+ cur_offset += len;
+
+ item++;
+ }
+
+ free(buf);
+out:
+ return;
+
+out_short_read:
+ printk("ERROR: sys_array too short to read %u bytes at offset %u\n",
+ len, cur_offset);
+ free(buf);
+}
+
+static int empty_backup(struct btrfs_root_backup *backup)
+{
+ if (backup == NULL ||
+ (backup->tree_root == 0 &&
+ backup->tree_root_gen == 0))
+ return 1;
+ return 0;
+}
+
+static void print_root_backup(struct btrfs_root_backup *backup)
+{
+ printf("\t\tbackup_tree_root:\t%llu\tgen: %llu\tlevel: %d\n",
+ btrfs_backup_tree_root(backup),
+ btrfs_backup_tree_root_gen(backup),
+ btrfs_backup_tree_root_level(backup));
+ printf("\t\tbackup_chunk_root:\t%llu\tgen: %llu\tlevel: %d\n",
+ btrfs_backup_chunk_root(backup),
+ btrfs_backup_chunk_root_gen(backup),
+ btrfs_backup_chunk_root_level(backup));
+ printf("\t\tbackup_extent_root:\t%llu\tgen: %llu\tlevel: %d\n",
+ btrfs_backup_extent_root(backup),
+ btrfs_backup_extent_root_gen(backup),
+ btrfs_backup_extent_root_level(backup));
+ printf("\t\tbackup_fs_root:\t\t%llu\tgen: %llu\tlevel: %d\n",
+ btrfs_backup_fs_root(backup),
+ btrfs_backup_fs_root_gen(backup),
+ btrfs_backup_fs_root_level(backup));
+ printf("\t\tbackup_dev_root:\t%llu\tgen: %llu\tlevel: %d\n",
+ btrfs_backup_dev_root(backup),
+ btrfs_backup_dev_root_gen(backup),
+ btrfs_backup_dev_root_level(backup));
+ printf("\t\tbackup_csum_root:\t%llu\tgen: %llu\tlevel: %d\n",
+ btrfs_backup_csum_root(backup),
+ btrfs_backup_csum_root_gen(backup),
+ btrfs_backup_csum_root_level(backup));
+
+ printf("\t\tbackup_total_bytes:\t%llu\n",
+ btrfs_backup_total_bytes(backup));
+ printf("\t\tbackup_bytes_used:\t%llu\n",
+ btrfs_backup_bytes_used(backup));
+ printf("\t\tbackup_num_devices:\t%llu\n",
+ btrfs_backup_num_devices(backup));
+ putchar('\n');
+}
+
+static void print_backup_roots(struct btrfs_super_block *sb)
+{
+ struct btrfs_root_backup *backup;
+ int i;
+
+ for (i = 0; i < BTRFS_NUM_BACKUP_ROOTS; i++) {
+ backup = sb->super_roots + i;
+ if (!empty_backup(backup)) {
+ printf("\tbackup %d:\n", i);
+ print_root_backup(backup);
+ }
+ }
+}
+
+struct readable_flag_entry {
+ u64 bit;
+ char *output;
+};
+
+#define DEF_INCOMPAT_FLAG_ENTRY(bit_name) \
+ {BTRFS_FEATURE_INCOMPAT_##bit_name, #bit_name}
+
+static struct readable_flag_entry incompat_flags_array[] = {
+ DEF_INCOMPAT_FLAG_ENTRY(MIXED_BACKREF),
+ DEF_INCOMPAT_FLAG_ENTRY(DEFAULT_SUBVOL),
+ DEF_INCOMPAT_FLAG_ENTRY(MIXED_GROUPS),
+ DEF_INCOMPAT_FLAG_ENTRY(COMPRESS_LZO),
+ DEF_INCOMPAT_FLAG_ENTRY(COMPRESS_LZOv2),
+ DEF_INCOMPAT_FLAG_ENTRY(BIG_METADATA),
+ DEF_INCOMPAT_FLAG_ENTRY(EXTENDED_IREF),
+ DEF_INCOMPAT_FLAG_ENTRY(RAID56),
+ DEF_INCOMPAT_FLAG_ENTRY(SKINNY_METADATA),
+ DEF_INCOMPAT_FLAG_ENTRY(NO_HOLES)
+};
+static const int incompat_flags_num = sizeof(incompat_flags_array) /
+ sizeof(struct readable_flag_entry);
+
+#define DEF_HEADER_FLAG_ENTRY(bit_name) \
+ {BTRFS_HEADER_FLAG_##bit_name, #bit_name}
+#define DEF_SUPER_FLAG_ENTRY(bit_name) \
+ {BTRFS_SUPER_FLAG_##bit_name, #bit_name}
+
+static struct readable_flag_entry super_flags_array[] = {
+ DEF_HEADER_FLAG_ENTRY(WRITTEN),
+ DEF_HEADER_FLAG_ENTRY(RELOC),
+ DEF_SUPER_FLAG_ENTRY(CHANGING_FSID),
+ DEF_SUPER_FLAG_ENTRY(SEEDING),
+ DEF_SUPER_FLAG_ENTRY(METADUMP),
+ DEF_SUPER_FLAG_ENTRY(METADUMP_V2)
+};
+static const int super_flags_num = ARRAY_SIZE(super_flags_array);
+
+#define BTRFS_SUPER_FLAG_SUPP (BTRFS_HEADER_FLAG_WRITTEN |\
+ BTRFS_HEADER_FLAG_RELOC |\
+ BTRFS_SUPER_FLAG_CHANGING_FSID |\
+ BTRFS_SUPER_FLAG_SEEDING |\
+ BTRFS_SUPER_FLAG_METADUMP |\
+ BTRFS_SUPER_FLAG_METADUMP_V2)
+
+static void __print_readable_flag(u64 flag, struct readable_flag_entry *array,
+ int array_size, u64 supported_flags)
+{
+ int i;
+ int first = 1;
+ struct readable_flag_entry *entry;
+
+ if (!flag)
+ return;
+
+ printf("\t\t\t( ");
+ for (i = 0; i < array_size; i++) {
+ entry = array + i;
+ if (flag & entry->bit) {
+ if (first)
+ printf("%s ", entry->output);
+ else
+ printf("|\n\t\t\t %s ", entry->output);
+ first = 0;
+ }
+ }
+ flag &= ~supported_flags;
+ if (flag) {
+ if (first)
+ printf("unknown flag: 0x%llx ", flag);
+ else
+ printf("|\n\t\t\t unknown flag: 0x%llx ", flag);
+ }
+ printf(")\n");
+}
+
+static void print_readable_incompat_flag(u64 flag)
+{
+ return __print_readable_flag(flag, incompat_flags_array,
+ incompat_flags_num,
+ BTRFS_FEATURE_INCOMPAT_SUPP);
+}
+
+static void print_readable_super_flag(u64 flag)
+{
+ return __print_readable_flag(flag, super_flags_array,
+ super_flags_num, BTRFS_SUPER_FLAG_SUPP);
+}
+
+static void dump_superblock(struct btrfs_super_block *sb, int full)
+{
+ int i;
+ char *s, buf[BTRFS_UUID_UNPARSED_SIZE];
+ u8 *p;
+
+ printf("csum\t\t\t0x");
+ for (i = 0, p = sb->csum; i < btrfs_super_csum_size(sb); i++)
+ printf("%02x", p[i]);
+ if (check_csum_sblock(sb, btrfs_super_csum_size(sb)))
+ printf(" [match]");
+ else
+ printf(" [DON'T MATCH]");
+ putchar('\n');
+
+ printf("bytenr\t\t\t%llu\n",
+ (unsigned long long)btrfs_super_bytenr(sb));
+ printf("flags\t\t\t0x%llx\n",
+ (unsigned long long)btrfs_super_flags(sb));
+ print_readable_super_flag(btrfs_super_flags(sb));
+
+ printf("magic\t\t\t");
+ s = (char *) &sb->magic;
+ for (i = 0; i < 8; i++)
+ putchar(isprint(s[i]) ? s[i] : '.');
+ if (btrfs_super_magic(sb) == BTRFS_MAGIC)
+ printf(" [match]\n");
+ else
+ printf(" [DON'T MATCH]\n");
+
+ uuid_unparse(sb->fsid, buf);
+ printf("fsid\t\t\t%s\n", buf);
+
+ printf("label\t\t\t");
+ s = sb->label;
+ for (i = 0; i < BTRFS_LABEL_SIZE && s[i]; i++)
+ putchar(isprint(s[i]) ? s[i] : '.');
+ putchar('\n');
+
+ printf("generation\t\t%llu\n",
+ (unsigned long long)btrfs_super_generation(sb));
+ printf("root\t\t\t%llu\n", (unsigned long long)btrfs_super_root(sb));
+ printf("sys_array_size\t\t%llu\n",
+ (unsigned long long)btrfs_super_sys_array_size(sb));
+ printf("chunk_root_generation\t%llu\n",
+ (unsigned long long)btrfs_super_chunk_root_generation(sb));
+ printf("root_level\t\t%llu\n",
+ (unsigned long long)btrfs_super_root_level(sb));
+ printf("chunk_root\t\t%llu\n",
+ (unsigned long long)btrfs_super_chunk_root(sb));
+ printf("chunk_root_level\t%llu\n",
+ (unsigned long long)btrfs_super_chunk_root_level(sb));
+ printf("log_root\t\t%llu\n",
+ (unsigned long long)btrfs_super_log_root(sb));
+ printf("log_root_transid\t%llu\n",
+ (unsigned long long)btrfs_super_log_root_transid(sb));
+ printf("log_root_level\t\t%llu\n",
+ (unsigned long long)btrfs_super_log_root_level(sb));
+ printf("total_bytes\t\t%llu\n",
+ (unsigned long long)btrfs_super_total_bytes(sb));
+ printf("bytes_used\t\t%llu\n",
+ (unsigned long long)btrfs_super_bytes_used(sb));
+ printf("sectorsize\t\t%llu\n",
+ (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("stripesize\t\t%llu\n",
+ (unsigned long long)btrfs_super_stripesize(sb));
+ printf("root_dir\t\t%llu\n",
+ (unsigned long long)btrfs_super_root_dir(sb));
+ printf("num_devices\t\t%llu\n",
+ (unsigned long long)btrfs_super_num_devices(sb));
+ printf("compat_flags\t\t0x%llx\n",
+ (unsigned long long)btrfs_super_compat_flags(sb));
+ printf("compat_ro_flags\t\t0x%llx\n",
+ (unsigned long long)btrfs_super_compat_ro_flags(sb));
+ printf("incompat_flags\t\t0x%llx\n",
+ (unsigned long long)btrfs_super_incompat_flags(sb));
+ print_readable_incompat_flag(btrfs_super_incompat_flags(sb));
+ printf("csum_type\t\t%llu\n",
+ (unsigned long long)btrfs_super_csum_type(sb));
+ printf("csum_size\t\t%llu\n",
+ (unsigned long long)btrfs_super_csum_size(sb));
+ printf("cache_generation\t%llu\n",
+ (unsigned long long)btrfs_super_cache_generation(sb));
+ printf("uuid_tree_generation\t%llu\n",
+ (unsigned long long)btrfs_super_uuid_tree_generation(sb));
+
+ uuid_unparse(sb->dev_item.uuid, buf);
+ printf("dev_item.uuid\t\t%s\n", buf);
+
+ uuid_unparse(sb->dev_item.fsid, buf);
+ printf("dev_item.fsid\t\t%s %s\n", buf,
+ !memcmp(sb->dev_item.fsid, sb->fsid, BTRFS_FSID_SIZE) ?
+ "[match]" : "[DON'T MATCH]");
+
+ printf("dev_item.type\t\t%llu\n", (unsigned long long)
+ btrfs_stack_device_type(&sb->dev_item));
+ printf("dev_item.total_bytes\t%llu\n", (unsigned long long)
+ btrfs_stack_device_total_bytes(&sb->dev_item));
+ printf("dev_item.bytes_used\t%llu\n", (unsigned long long)
+ btrfs_stack_device_bytes_used(&sb->dev_item));
+ printf("dev_item.io_align\t%u\n", (unsigned int)
+ btrfs_stack_device_io_align(&sb->dev_item));
+ printf("dev_item.io_width\t%u\n", (unsigned int)
+ btrfs_stack_device_io_width(&sb->dev_item));
+ printf("dev_item.sector_size\t%u\n", (unsigned int)
+ btrfs_stack_device_sector_size(&sb->dev_item));
+ printf("dev_item.devid\t\t%llu\n",
+ btrfs_stack_device_id(&sb->dev_item));
+ printf("dev_item.dev_group\t%u\n", (unsigned int)
+ btrfs_stack_device_group(&sb->dev_item));
+ printf("dev_item.seek_speed\t%u\n", (unsigned int)
+ btrfs_stack_device_seek_speed(&sb->dev_item));
+ printf("dev_item.bandwidth\t%u\n", (unsigned int)
+ btrfs_stack_device_bandwidth(&sb->dev_item));
+ printf("dev_item.generation\t%llu\n", (unsigned long long)
+ btrfs_stack_device_generation(&sb->dev_item));
+ if (full) {
+ printf("sys_chunk_array[%d]:\n", BTRFS_SYSTEM_CHUNK_ARRAY_SIZE);
+ print_sys_chunk_array(sb);
+ printf("backup_roots[%d]:\n", BTRFS_NUM_BACKUP_ROOTS);
+ print_backup_roots(sb);
+ }
+}
+
+static int load_and_dump_sb(char *filename, int fd, u64 sb_bytenr, int full,
+ int force)
+{
+ u8 super_block_data[BTRFS_SUPER_INFO_SIZE];
+ struct btrfs_super_block *sb;
+ u64 ret;
+
+ sb = (struct btrfs_super_block *)super_block_data;
+
+ ret = pread64(fd, super_block_data, BTRFS_SUPER_INFO_SIZE, sb_bytenr);
+ if (ret != BTRFS_SUPER_INFO_SIZE) {
+ /* check if the disk if too short for further superblock */
+ if (ret == 0 && errno == 0)
+ return 0;
+
+ error("failed to read the superblock on %s at %llu",
+ filename, (unsigned long long)sb_bytenr);
+ error("error = '%s', errno = %d", strerror(errno), errno);
+ return 1;
+ }
+ printf("superblock: bytenr=%llu, device=%s\n", sb_bytenr, filename);
+ printf("---------------------------------------------------------\n");
+ if (btrfs_super_magic(sb) != BTRFS_MAGIC && !force) {
+ error("bad magic on superblock on %s at %llu",
+ filename, (unsigned long long)sb_bytenr);
+ } else {
+ dump_superblock(sb, full);
+ }
+ return 0;
+}
+
+const char * const cmd_inspect_dump_super_usage[] = {
+ "btrfs inspect-internal dump-super [options] device [device...]",
+ "Dump superblock from a device in a textual form",
+ "-f|--full print full superblock information",
+ "-a|--all print information about all superblocks",
+ "-i <super_mirror> specify which mirror to print out",
+ "-F|--force attempt to dump superblocks with bad magic",
+ "-s <bytenr> specify alternate superblock offset",
+ NULL
+};
+
+int cmd_inspect_dump_super(int argc, char **argv)
+{
+ int all = 0;
+ int full = 0;
+ int force = 0;
+ char *filename;
+ int fd = -1;
+ int i;
+ int ret = 0;
+ u64 arg;
+ u64 sb_bytenr = btrfs_sb_offset(0);
+
+ while (1) {
+ int c;
+ static const struct option long_options[] = {
+ {"all", no_argument, NULL, 'a'},
+ {"full", no_argument, NULL, 'f'},
+ {"force", no_argument, NULL, 'F'},
+ {NULL, 0, NULL, 0}
+ };
+
+ c = getopt_long(argc, argv, "fFai:s:", long_options, NULL);
+ if (c < 0)
+ break;
+
+ switch (c) {
+ case 'i':
+ arg = arg_strtou64(optarg);
+ if (arg >= BTRFS_SUPER_MIRROR_MAX) {
+ error("super mirror too big: %llu >= %d",
+ arg, BTRFS_SUPER_MIRROR_MAX);
+ usage(cmd_inspect_dump_super_usage);
+ }
+ sb_bytenr = btrfs_sb_offset(arg);
+ break;
+
+ case 'a':
+ all = 1;
+ break;
+ case 'f':
+ full = 1;
+ break;
+ case 'F':
+ force = 1;
+ break;
+ case 's':
+ sb_bytenr = arg_strtou64(optarg);
+ all = 0;
+ break;
+ default:
+ usage(cmd_inspect_dump_super_usage);
+ }
+ }
+
+ if (check_argc_min(argc - optind, 1))
+ usage(cmd_inspect_dump_super_usage);
+
+ for (i = optind; i < argc; i++) {
+ filename = argv[i];
+ fd = open(filename, O_RDONLY, 0666);
+ if (fd < 0) {
+ error("cannot open %s: %s", filename, strerror(errno));
+ ret = 1;
+ goto out;
+ }
+
+ if (all) {
+ int idx;
+
+ for (idx = 0; idx < BTRFS_SUPER_MIRROR_MAX; idx++) {
+ sb_bytenr = btrfs_sb_offset(idx);
+ if (load_and_dump_sb(filename, fd,
+ sb_bytenr, full, force)) {
+ close(fd);
+ ret = 1;
+ goto out;
+ }
+
+ putchar('\n');
+ }
+ } else {
+ load_and_dump_sb(filename, fd, sb_bytenr, full, force);
+ putchar('\n');
+ }
+ close(fd);
+ }
+
+out:
+ return ret;
+}
diff --git a/cmds-inspect-dump-super.h b/cmds-inspect-dump-super.h
new file mode 100644
index 00000000..ffab81d7
--- /dev/null
+++ b/cmds-inspect-dump-super.h
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+#ifndef __CMDS_INSPECT_DUMP_SUPER_H__
+#define __CMDS_INSPECT_DUMP_SUPER_H__
+
+int cmd_inspect_dump_super(int argc, char **argv);
+
+extern const char * const cmd_inspect_dump_super_usage[];
+
+#endif
diff --git a/cmds-inspect-dump-tree.c b/cmds-inspect-dump-tree.c
new file mode 100644
index 00000000..5e206345
--- /dev/null
+++ b/cmds-inspect-dump-tree.c
@@ -0,0 +1,551 @@
+/*
+ * Copyright (C) 2007 Oracle. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <uuid/uuid.h>
+#include <getopt.h>
+
+#include "kerncompat.h"
+#include "radix-tree.h"
+#include "ctree.h"
+#include "disk-io.h"
+#include "print-tree.h"
+#include "transaction.h"
+#include "volumes.h"
+#include "commands.h"
+#include "utils.h"
+#include "cmds-inspect-dump-tree.h"
+
+static void print_extents(struct btrfs_root *root, struct extent_buffer *eb)
+{
+ int i;
+ u32 nr;
+ u32 size;
+
+ if (!eb)
+ return;
+
+ if (btrfs_is_leaf(eb)) {
+ btrfs_print_leaf(root, eb);
+ return;
+ }
+
+ size = root->nodesize;
+ nr = btrfs_header_nritems(eb);
+ for (i = 0; i < nr; i++) {
+ struct extent_buffer *next = read_tree_block(root,
+ btrfs_node_blockptr(eb, i),
+ size,
+ btrfs_node_ptr_generation(eb, i));
+ if (!extent_buffer_uptodate(next))
+ continue;
+ if (btrfs_is_leaf(next) &&
+ btrfs_header_level(eb) != 1)
+ BUG();
+ if (btrfs_header_level(next) !=
+ btrfs_header_level(eb) - 1)
+ BUG();
+ print_extents(root, next);
+ free_extent_buffer(next);
+ }
+}
+
+static void print_old_roots(struct btrfs_super_block *super)
+{
+ struct btrfs_root_backup *backup;
+ int i;
+
+ for (i = 0; i < BTRFS_NUM_BACKUP_ROOTS; i++) {
+ backup = super->super_roots + i;
+ printf("btrfs root backup slot %d\n", i);
+ printf("\ttree root gen %llu block %llu\n",
+ (unsigned long long)btrfs_backup_tree_root_gen(backup),
+ (unsigned long long)btrfs_backup_tree_root(backup));
+
+ printf("\t\textent root gen %llu block %llu\n",
+ (unsigned long long)btrfs_backup_extent_root_gen(backup),
+ (unsigned long long)btrfs_backup_extent_root(backup));
+
+ printf("\t\tchunk root gen %llu block %llu\n",
+ (unsigned long long)btrfs_backup_chunk_root_gen(backup),
+ (unsigned long long)btrfs_backup_chunk_root(backup));
+
+ printf("\t\tdevice root gen %llu block %llu\n",
+ (unsigned long long)btrfs_backup_dev_root_gen(backup),
+ (unsigned long long)btrfs_backup_dev_root(backup));
+
+ printf("\t\tcsum root gen %llu block %llu\n",
+ (unsigned long long)btrfs_backup_csum_root_gen(backup),
+ (unsigned long long)btrfs_backup_csum_root(backup));
+
+ printf("\t\tfs root gen %llu block %llu\n",
+ (unsigned long long)btrfs_backup_fs_root_gen(backup),
+ (unsigned long long)btrfs_backup_fs_root(backup));
+
+ printf("\t\t%llu used %llu total %llu devices\n",
+ (unsigned long long)btrfs_backup_bytes_used(backup),
+ (unsigned long long)btrfs_backup_total_bytes(backup),
+ (unsigned long long)btrfs_backup_num_devices(backup));
+ }
+}
+
+/*
+ * Convert a tree name from various forms to the numerical id if possible
+ * Accepted forms:
+ * - case does not matter
+ * - same as the key name, BTRFS_ROOT_TREE_OBJECTID
+ * - dtto shortened, BTRFS_ROOT_TREE
+ * - dtto without prefix, ROOT_TREE
+ * - common name, ROOT, CHUNK, EXTENT, ...
+ * - dtto alias, DEVICE for DEV, CHECKSUM for CSUM
+ *
+ * Returns 0 if the tree id was not recognized.
+ */
+static u64 treeid_from_string(const char *str, const char **end)
+{
+ int match = 0;
+ int i;
+ u64 id;
+ static struct treename {
+ const char *name;
+ u64 id;
+ } tn[] = {
+ { "ROOT", BTRFS_ROOT_TREE_OBJECTID },
+ { "EXTENT", BTRFS_EXTENT_TREE_OBJECTID },
+ { "CHUNK", BTRFS_CHUNK_TREE_OBJECTID },
+ { "DEVICE", BTRFS_DEV_TREE_OBJECTID },
+ { "DEV", BTRFS_DEV_TREE_OBJECTID },
+ { "FS_TREE", BTRFS_FS_TREE_OBJECTID },
+ { "CSUM", BTRFS_CSUM_TREE_OBJECTID },
+ { "CHECKSUM", BTRFS_CSUM_TREE_OBJECTID },
+ { "QUOTA", BTRFS_QUOTA_TREE_OBJECTID },
+ { "UUID", BTRFS_UUID_TREE_OBJECTID },
+ { "FREE_SPACE", BTRFS_FREE_SPACE_TREE_OBJECTID },
+ { "TREE_LOG_FIXUP", BTRFS_TREE_LOG_FIXUP_OBJECTID },
+ { "TREE_LOG", BTRFS_TREE_LOG_OBJECTID },
+ { "TREE_RELOC", BTRFS_TREE_RELOC_OBJECTID },
+ { "DATA_RELOC", BTRFS_DATA_RELOC_TREE_OBJECTID }
+ };
+
+ if (strncasecmp("BTRFS_", str, strlen("BTRFS_")) == 0)
+ str += strlen("BTRFS_");
+
+ for (i = 0; i < ARRAY_SIZE(tn); i++) {
+ int len = strlen(tn[i].name);
+
+ if (strncasecmp(tn[i].name, str, len) == 0) {
+ id = tn[i].id;
+ match = 1;
+ str += len;
+ break;
+ }
+ }
+
+ if (!match)
+ return 0;
+
+ if (strncasecmp("_TREE", str, strlen("_TREE")) == 0)
+ str += strlen("_TREE");
+
+ if (strncasecmp("_OBJECTID", str, strlen("_OBJECTID")) == 0)
+ str += strlen("_OBJECTID");
+
+ *end = str;
+
+ return id;
+}
+
+const char * const cmd_inspect_dump_tree_usage[] = {
+ "btrfs inspect-internal dump-tree [options] device",
+ "Dump tree structures from a given device",
+ "Dump tree structures from a given device in textual form, expand keys to human",
+ "readable equivalents where possible.",
+ "Note: contains file names, consider that if you're asked to send the dump",
+ "for analysis.",
+ "",
+ "-e|--extents print only extent info: extent and device trees",
+ "-d|--device print only device info: tree root, chunk and device trees",
+ "-r|--roots print only short root node info",
+ "-R|--backups same as --roots plus print backup root info",
+ "-u|--uuid print only the uuid tree",
+ "-b|--block <block_num> print info from the specified block only",
+ "-t|--tree <tree_id> print only tree with the given id (string or number)",
+ NULL
+};
+
+int cmd_inspect_dump_tree(int argc, char **argv)
+{
+ struct btrfs_root *root;
+ struct btrfs_fs_info *info;
+ struct btrfs_path path;
+ struct btrfs_key key;
+ struct btrfs_root_item ri;
+ struct extent_buffer *leaf;
+ struct btrfs_disk_key disk_key;
+ struct btrfs_key found_key;
+ char uuidbuf[BTRFS_UUID_UNPARSED_SIZE];
+ int ret;
+ int slot;
+ int extent_only = 0;
+ int device_only = 0;
+ int uuid_tree_only = 0;
+ int roots_only = 0;
+ int root_backups = 0;
+ u64 block_only = 0;
+ struct btrfs_root *tree_root_scan;
+ u64 tree_id = 0;
+
+ while (1) {
+ int c;
+ static const struct option long_options[] = {
+ { "extents", no_argument, NULL, 'e'},
+ { "device", no_argument, NULL, 'd'},
+ { "roots", no_argument, NULL, 'r'},
+ { "backups", no_argument, NULL, 'R'},
+ { "uuid", no_argument, NULL, 'u'},
+ { "block", required_argument, NULL, 'b'},
+ { "tree", required_argument, NULL, 't'},
+ { NULL, 0, NULL, 0 }
+ };
+
+ c = getopt_long(argc, argv, "deb:rRut:", long_options, NULL);
+ if (c < 0)
+ break;
+ switch (c) {
+ case 'e':
+ extent_only = 1;
+ break;
+ case 'd':
+ device_only = 1;
+ break;
+ case 'r':
+ roots_only = 1;
+ break;
+ case 'u':
+ uuid_tree_only = 1;
+ break;
+ case 'R':
+ roots_only = 1;
+ root_backups = 1;
+ break;
+ case 'b':
+ block_only = arg_strtou64(optarg);
+ break;
+ case 't':
+ if (string_is_numerical(optarg)) {
+ tree_id = arg_strtou64(optarg);
+ } else {
+ const char *end = NULL;
+
+ tree_id = treeid_from_string(optarg, &end);
+
+ if (*end) {
+ error("unexpected tree id suffix of '%s': %s\n",
+ optarg, end);
+ exit(1);
+ }
+ }
+ if (!tree_id) {
+ error("unrecognized tree id: %s\n",
+ optarg);
+ exit(1);
+ }
+ break;
+ default:
+ usage(cmd_inspect_dump_tree_usage);
+ }
+ }
+
+ if (check_argc_exact(argc - optind, 1))
+ usage(cmd_inspect_dump_tree_usage);
+
+ ret = check_arg_type(argv[optind]);
+ if (ret != BTRFS_ARG_BLKDEV && ret != BTRFS_ARG_REG) {
+ error("not a block device or regular file: %s", argv[optind]);
+ goto out;
+ }
+
+ printf("%s\n", PACKAGE_STRING);
+
+ info = open_ctree_fs_info(argv[optind], 0, 0, 0, OPEN_CTREE_PARTIAL);
+ if (!info) {
+ error("unable to open %s", argv[optind]);
+ goto out;
+ }
+
+ root = info->fs_root;
+ if (!root) {
+ error("unable to open %s", argv[optind]);
+ goto out;
+ }
+
+ if (block_only) {
+ leaf = read_tree_block(root,
+ block_only,
+ root->nodesize, 0);
+
+ if (extent_buffer_uptodate(leaf) &&
+ btrfs_header_level(leaf) != 0) {
+ free_extent_buffer(leaf);
+ leaf = NULL;
+ }
+
+ if (!leaf) {
+ leaf = read_tree_block(root,
+ block_only,
+ root->nodesize, 0);
+ }
+ if (!extent_buffer_uptodate(leaf)) {
+ error("failed to read %llu",
+ (unsigned long long)block_only);
+ goto close_root;
+ }
+ btrfs_print_tree(root, leaf, 0);
+ free_extent_buffer(leaf);
+ goto close_root;
+ }
+
+ if (!(extent_only || uuid_tree_only || tree_id)) {
+ if (roots_only) {
+ printf("root tree: %llu level %d\n",
+ (unsigned long long)info->tree_root->node->start,
+ btrfs_header_level(info->tree_root->node));
+ printf("chunk tree: %llu level %d\n",
+ (unsigned long long)info->chunk_root->node->start,
+ btrfs_header_level(info->chunk_root->node));
+ } else {
+ if (info->tree_root->node) {
+ printf("root tree\n");
+ btrfs_print_tree(info->tree_root,
+ info->tree_root->node, 1);
+ }
+
+ if (info->chunk_root->node) {
+ printf("chunk tree\n");
+ btrfs_print_tree(info->chunk_root,
+ info->chunk_root->node, 1);
+ }
+ }
+ }
+ tree_root_scan = info->tree_root;
+
+ btrfs_init_path(&path);
+again:
+ if (!extent_buffer_uptodate(tree_root_scan->node))
+ goto no_node;
+
+ /*
+ * Tree's that are not pointed by the tree of tree roots
+ */
+ if (tree_id && tree_id == BTRFS_ROOT_TREE_OBJECTID) {
+ if (!info->tree_root->node) {
+ error("cannot print root tree, invalid pointer");
+ goto no_node;
+ }
+ printf("root tree\n");
+ btrfs_print_tree(info->tree_root, info->tree_root->node, 1);
+ goto no_node;
+ }
+
+ if (tree_id && tree_id == BTRFS_CHUNK_TREE_OBJECTID) {
+ if (!info->chunk_root->node) {
+ error("cannot print chunk tree, invalid pointer");
+ goto no_node;
+ }
+ printf("chunk tree\n");
+ btrfs_print_tree(info->chunk_root, info->chunk_root->node, 1);
+ goto no_node;
+ }
+
+ key.offset = 0;
+ key.objectid = 0;
+ btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY);
+ ret = btrfs_search_slot(NULL, tree_root_scan, &key, &path, 0, 0);
+ BUG_ON(ret < 0);
+ while (1) {
+ leaf = path.nodes[0];
+ slot = path.slots[0];
+ if (slot >= btrfs_header_nritems(leaf)) {
+ ret = btrfs_next_leaf(tree_root_scan, &path);
+ if (ret != 0)
+ break;
+ leaf = path.nodes[0];
+ slot = path.slots[0];
+ }
+ btrfs_item_key(leaf, &disk_key, path.slots[0]);
+ btrfs_disk_key_to_cpu(&found_key, &disk_key);
+ if (btrfs_key_type(&found_key) == BTRFS_ROOT_ITEM_KEY) {
+ unsigned long offset;
+ struct extent_buffer *buf;
+ int skip = extent_only | device_only | uuid_tree_only;
+
+ offset = btrfs_item_ptr_offset(leaf, slot);
+ read_extent_buffer(leaf, &ri, offset, sizeof(ri));
+ buf = read_tree_block(tree_root_scan,
+ btrfs_root_bytenr(&ri),
+ tree_root_scan->nodesize,
+ 0);
+ if (!extent_buffer_uptodate(buf))
+ goto next;
+ if (tree_id && found_key.objectid != tree_id) {
+ free_extent_buffer(buf);
+ goto next;
+ }
+
+ switch (found_key.objectid) {
+ case BTRFS_ROOT_TREE_OBJECTID:
+ if (!skip)
+ printf("root");
+ break;
+ case BTRFS_EXTENT_TREE_OBJECTID:
+ if (!device_only && !uuid_tree_only)
+ skip = 0;
+ if (!skip)
+ printf("extent");
+ break;
+ case BTRFS_CHUNK_TREE_OBJECTID:
+ if (!skip) {
+ printf("chunk");
+ }
+ break;
+ case BTRFS_DEV_TREE_OBJECTID:
+ if (!uuid_tree_only)
+ skip = 0;
+ if (!skip)
+ printf("device");
+ break;
+ case BTRFS_FS_TREE_OBJECTID:
+ if (!skip) {
+ printf("fs");
+ }
+ break;
+ case BTRFS_ROOT_TREE_DIR_OBJECTID:
+ skip = 0;
+ printf("directory");
+ break;
+ case BTRFS_CSUM_TREE_OBJECTID:
+ if (!skip) {
+ printf("checksum");
+ }
+ break;
+ case BTRFS_ORPHAN_OBJECTID:
+ if (!skip) {
+ printf("orphan");
+ }
+ break;
+ case BTRFS_TREE_LOG_OBJECTID:
+ if (!skip) {
+ printf("log");
+ }
+ break;
+ case BTRFS_TREE_LOG_FIXUP_OBJECTID:
+ if (!skip) {
+ printf("log fixup");
+ }
+ break;
+ case BTRFS_TREE_RELOC_OBJECTID:
+ if (!skip) {
+ printf("reloc");
+ }
+ break;
+ case BTRFS_DATA_RELOC_TREE_OBJECTID:
+ if (!skip) {
+ printf("data reloc");
+ }
+ break;
+ case BTRFS_EXTENT_CSUM_OBJECTID:
+ if (!skip) {
+ printf("extent checksum");
+ }
+ break;
+ case BTRFS_QUOTA_TREE_OBJECTID:
+ if (!skip) {
+ printf("quota");
+ }
+ break;
+ case BTRFS_UUID_TREE_OBJECTID:
+ if (!extent_only && !device_only)
+ skip = 0;
+ if (!skip)
+ printf("uuid");
+ break;
+ case BTRFS_FREE_SPACE_TREE_OBJECTID:
+ if (!skip)
+ printf("free space");
+ break;
+ case BTRFS_MULTIPLE_OBJECTIDS:
+ if (!skip) {
+ printf("multiple");
+ }
+ break;
+ default:
+ if (!skip) {
+ printf("file");
+ }
+ }
+ if (extent_only && !skip) {
+ printf(" tree ");
+ btrfs_print_key(&disk_key);
+ printf("\n");
+ print_extents(tree_root_scan, buf);
+ } else if (!skip) {
+ printf(" tree ");
+ btrfs_print_key(&disk_key);
+ if (roots_only) {
+ printf(" %llu level %d\n",
+ (unsigned long long)buf->start,
+ btrfs_header_level(buf));
+ } else {
+ printf(" \n");
+ btrfs_print_tree(tree_root_scan, buf, 1);
+ }
+ }
+ free_extent_buffer(buf);
+ }
+next:
+ path.slots[0]++;
+ }
+no_node:
+ btrfs_release_path(&path);
+
+ if (tree_root_scan == info->tree_root &&
+ info->log_root_tree) {
+ tree_root_scan = info->log_root_tree;
+ goto again;
+ }
+
+ if (extent_only || device_only || uuid_tree_only)
+ goto close_root;
+
+ if (root_backups)
+ print_old_roots(info->super_copy);
+
+ printf("total bytes %llu\n",
+ (unsigned long long)btrfs_super_total_bytes(info->super_copy));
+ printf("bytes used %llu\n",
+ (unsigned long long)btrfs_super_bytes_used(info->super_copy));
+ uuidbuf[BTRFS_UUID_UNPARSED_SIZE - 1] = '\0';
+ uuid_unparse(info->super_copy->fsid, uuidbuf);
+ printf("uuid %s\n", uuidbuf);
+close_root:
+ ret = close_ctree(root);
+out:
+ return !!ret;
+}
diff --git a/cmds-inspect-dump-tree.h b/cmds-inspect-dump-tree.h
new file mode 100644
index 00000000..755fb4b1
--- /dev/null
+++ b/cmds-inspect-dump-tree.h
@@ -0,0 +1,24 @@
+/*
+ * 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 __CMDS_INSPECT_DUMP_TREE_H__
+#define __CMDS_INSPECT_DUMP_TREE_H__
+
+int cmd_inspect_dump_tree(int argc, char **argv);
+
+extern const char * const cmd_inspect_dump_tree_usage[];
+
+#endif
diff --git a/cmds-inspect-tree-stats.c b/cmds-inspect-tree-stats.c
new file mode 100644
index 00000000..e80041df
--- /dev/null
+++ b/cmds-inspect-tree-stats.c
@@ -0,0 +1,506 @@
+/*
+ * Copyright (C) 2011 Red Hat. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <zlib.h>
+
+#include "kerncompat.h"
+#include "ctree.h"
+#include "disk-io.h"
+#include "print-tree.h"
+#include "transaction.h"
+#include "list.h"
+#include "volumes.h"
+#include "utils.h"
+#include "commands.h"
+#include "cmds-inspect-tree-stats.h"
+
+static int verbose = 0;
+static int no_pretty = 0;
+
+struct seek {
+ u64 distance;
+ u64 count;
+ struct rb_node n;
+};
+
+struct root_stats {
+ u64 total_nodes;
+ u64 total_leaves;
+ u64 total_bytes;
+ u64 total_inline;
+ u64 total_seeks;
+ u64 forward_seeks;
+ u64 backward_seeks;
+ u64 total_seek_len;
+ u64 max_seek_len;
+ u64 total_clusters;
+ u64 total_cluster_size;
+ u64 min_cluster_size;
+ u64 max_cluster_size;
+ u64 lowest_bytenr;
+ u64 highest_bytenr;
+ struct rb_root seek_root;
+ int total_levels;
+};
+
+static int add_seek(struct rb_root *root, u64 dist)
+{
+ struct rb_node **p = &root->rb_node;
+ struct rb_node *parent = NULL;
+ struct seek *seek = NULL;
+
+ while (*p) {
+ parent = *p;
+ seek = rb_entry(parent, struct seek, n);
+
+ if (dist < seek->distance) {
+ p = &(*p)->rb_left;
+ } else if (dist > seek->distance) {
+ p = &(*p)->rb_right;
+ } else {
+ seek->count++;
+ return 0;
+ }
+ }
+
+ seek = malloc(sizeof(struct seek));
+ if (!seek)
+ return -ENOMEM;
+ seek->distance = dist;
+ seek->count = 1;
+ rb_link_node(&seek->n, parent, p);
+ rb_insert_color(&seek->n, root);
+ return 0;
+}
+
+static int walk_leaf(struct btrfs_root *root, struct btrfs_path *path,
+ struct root_stats *stat, int find_inline)
+{
+ struct extent_buffer *b = path->nodes[0];
+ struct btrfs_file_extent_item *fi;
+ struct btrfs_key found_key;
+ int i;
+
+ stat->total_bytes += root->nodesize;
+ stat->total_leaves++;
+
+ if (!find_inline)
+ return 0;
+
+ for (i = 0; i < btrfs_header_nritems(b); i++) {
+ btrfs_item_key_to_cpu(b, &found_key, i);
+ if (found_key.type != BTRFS_EXTENT_DATA_KEY)
+ continue;
+
+ fi = btrfs_item_ptr(b, i, struct btrfs_file_extent_item);
+ if (btrfs_file_extent_type(b, fi) == BTRFS_FILE_EXTENT_INLINE)
+ stat->total_inline +=
+ btrfs_file_extent_inline_item_len(b,
+ btrfs_item_nr(i));
+ }
+
+ return 0;
+}
+
+static u64 calc_distance(u64 block1, u64 block2)
+{
+ if (block1 < block2)
+ return block2 - block1;
+ return block1 - block2;
+}
+
+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];
+ u64 last_block;
+ u64 cluster_size = root->nodesize;
+ int i;
+ int ret = 0;
+
+ stat->total_bytes += root->nodesize;
+ stat->total_nodes++;
+
+ last_block = btrfs_header_bytenr(b);
+ for (i = 0; i < btrfs_header_nritems(b); i++) {
+ struct extent_buffer *tmp = NULL;
+ u64 cur_blocknr = btrfs_node_blockptr(b, i);
+
+ path->slots[level] = i;
+ if ((level - 1) > 0 || find_inline) {
+ tmp = read_tree_block(root, cur_blocknr,
+ root->nodesize,
+ btrfs_node_ptr_generation(b, i));
+ if (!extent_buffer_uptodate(tmp)) {
+ fprintf(stderr, "Failed to read blocknr %llu\n",
+ btrfs_node_blockptr(b, i));
+ continue;
+ }
+ path->nodes[level - 1] = tmp;
+ }
+ if (level - 1)
+ ret = walk_nodes(root, path, stat, level - 1,
+ find_inline);
+ else
+ ret = walk_leaf(root, path, stat, find_inline);
+ if (last_block + root->nodesize != cur_blocknr) {
+ u64 distance = calc_distance(last_block +
+ root->nodesize,
+ cur_blocknr);
+ stat->total_seeks++;
+ stat->total_seek_len += distance;
+ if (stat->max_seek_len < distance)
+ stat->max_seek_len = distance;
+ if (add_seek(&stat->seek_root, distance)) {
+ fprintf(stderr, "Error adding new seek\n");
+ ret = -ENOMEM;
+ break;
+ }
+
+ if (last_block < cur_blocknr)
+ stat->forward_seeks++;
+ else
+ stat->backward_seeks++;
+ if (cluster_size != root->nodesize) {
+ stat->total_cluster_size += cluster_size;
+ stat->total_clusters++;
+ if (cluster_size < stat->min_cluster_size)
+ stat->min_cluster_size = cluster_size;
+ if (cluster_size > stat->max_cluster_size)
+ stat->max_cluster_size = cluster_size;
+ }
+ cluster_size = root->nodesize;
+ } else {
+ cluster_size += root->nodesize;
+ }
+ last_block = cur_blocknr;
+ if (cur_blocknr < stat->lowest_bytenr)
+ stat->lowest_bytenr = cur_blocknr;
+ if (cur_blocknr > stat->highest_bytenr)
+ stat->highest_bytenr = cur_blocknr;
+ free_extent_buffer(tmp);
+ if (ret) {
+ fprintf(stderr, "Error walking down path\n");
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static void print_seek_histogram(struct root_stats *stat)
+{
+ struct rb_node *n = rb_first(&stat->seek_root);
+ struct seek *seek;
+ u64 tick_interval;
+ u64 group_start = 0;
+ u64 group_count = 0;
+ u64 group_end = 0;
+ u64 i;
+ u64 max_seek = stat->max_seek_len;
+ int digits = 1;
+
+ if (stat->total_seeks < 20)
+ return;
+
+ while ((max_seek /= 10))
+ digits++;
+
+ /* Make a tick count as 5% of the total seeks */
+ tick_interval = stat->total_seeks / 20;
+ printf("\tSeek histogram\n");
+ for (; n; n = rb_next(n)) {
+ u64 ticks, gticks = 0;
+
+ seek = rb_entry(n, struct seek, n);
+ ticks = seek->count / tick_interval;
+ if (group_count)
+ gticks = group_count / tick_interval;
+
+ if (ticks <= 2 && gticks <= 2) {
+ if (group_count == 0)
+ group_start = seek->distance;
+ group_end = seek->distance;
+ group_count += seek->count;
+ continue;
+ }
+
+ if (group_count) {
+
+ gticks = group_count / tick_interval;
+ printf("\t\t%*Lu - %*Lu: %*Lu ", digits, group_start,
+ digits, group_end, digits, group_count);
+ if (gticks) {
+ for (i = 0; i < gticks; i++)
+ printf("#");
+ printf("\n");
+ } else {
+ printf("|\n");
+ }
+ group_count = 0;
+ }
+
+ if (ticks <= 2)
+ continue;
+
+ printf("\t\t%*Lu - %*Lu: %*Lu ", digits, seek->distance,
+ digits, seek->distance, digits, seek->count);
+ for (i = 0; i < ticks; i++)
+ printf("#");
+ printf("\n");
+ }
+ if (group_count) {
+ u64 gticks;
+
+ gticks = group_count / tick_interval;
+ printf("\t\t%*Lu - %*Lu: %*Lu ", digits, group_start,
+ digits, group_end, digits, group_count);
+ if (gticks) {
+ for (i = 0; i < gticks; i++)
+ printf("#");
+ printf("\n");
+ } else {
+ printf("|\n");
+ }
+ group_count = 0;
+ }
+}
+
+static void timeval_subtract(struct timeval *result, struct timeval *x,
+ struct timeval *y)
+{
+ if (x->tv_usec < y->tv_usec) {
+ int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1;
+ y->tv_usec -= 1000000 * nsec;
+ y->tv_sec += nsec;
+ }
+
+ if (x->tv_usec - y->tv_usec > 1000000) {
+ int nsec = (x->tv_usec - y->tv_usec) / 1000000;
+ y->tv_usec += 1000000 * nsec;
+ y->tv_sec -= nsec;
+ }
+
+ result->tv_sec = x->tv_sec - y->tv_sec;
+ result->tv_usec = x->tv_usec - y->tv_usec;
+}
+
+static int calc_root_size(struct btrfs_root *tree_root, struct btrfs_key *key,
+ int find_inline)
+{
+ struct btrfs_root *root;
+ struct btrfs_path *path;
+ struct rb_node *n;
+ struct timeval start, end, diff = {0};
+ struct root_stats stat;
+ int level;
+ int ret = 0;
+ int size_fail = 0;
+
+ root = btrfs_read_fs_root(tree_root->fs_info, key);
+ if (IS_ERR(root)) {
+ fprintf(stderr, "Failed to read root %llu\n", key->objectid);
+ return 1;
+ }
+
+ path = btrfs_alloc_path();
+ if (!path) {
+ fprintf(stderr, "Could not allocate path\n");
+ return 1;
+ }
+
+ memset(&stat, 0, sizeof(stat));
+ level = btrfs_header_level(root->node);
+ stat.lowest_bytenr = btrfs_header_bytenr(root->node);
+ stat.highest_bytenr = stat.lowest_bytenr;
+ stat.min_cluster_size = (u64)-1;
+ stat.max_cluster_size = root->nodesize;
+ path->nodes[level] = root->node;
+ if (gettimeofday(&start, NULL)) {
+ fprintf(stderr, "Error getting time: %d\n", errno);
+ goto out;
+ }
+ if (!level) {
+ ret = walk_leaf(root, path, &stat, find_inline);
+ if (ret)
+ goto out;
+ goto out_print;
+ }
+
+ ret = walk_nodes(root, path, &stat, level, find_inline);
+ if (ret)
+ goto out;
+ if (gettimeofday(&end, NULL)) {
+ fprintf(stderr, "Error getting time: %d\n", errno);
+ goto out;
+ }
+ timeval_subtract(&diff, &end, &start);
+out_print:
+ if (stat.min_cluster_size == (u64)-1) {
+ stat.min_cluster_size = 0;
+ stat.total_clusters = 1;
+ }
+
+ if (no_pretty || size_fail) {
+ printf("\tTotal size: %llu\n", stat.total_bytes);
+ printf("\t\tInline data: %llu\n", stat.total_inline);
+ printf("\tTotal seeks: %llu\n", stat.total_seeks);
+ printf("\t\tForward seeks: %llu\n", stat.forward_seeks);
+ printf("\t\tBackward seeks: %llu\n", stat.backward_seeks);
+ printf("\t\tAvg seek len: %llu\n", stat.total_seeks ?
+ stat.total_seek_len / stat.total_seeks : 0);
+ print_seek_histogram(&stat);
+ printf("\tTotal clusters: %llu\n", stat.total_clusters);
+ printf("\t\tAvg cluster size: %llu\n", stat.total_cluster_size /
+ stat.total_clusters);
+ printf("\t\tMin cluster size: %llu\n", stat.min_cluster_size);
+ printf("\t\tMax cluster size: %llu\n", stat.max_cluster_size);
+ printf("\tTotal disk spread: %llu\n", stat.highest_bytenr -
+ stat.lowest_bytenr);
+ printf("\tTotal read time: %d s %d us\n", (int)diff.tv_sec,
+ (int)diff.tv_usec);
+ printf("\tLevels: %d\n", level + 1);
+ } else {
+ printf("\tTotal size: %s\n", pretty_size(stat.total_bytes));
+ printf("\t\tInline data: %s\n", pretty_size(stat.total_inline));
+ printf("\tTotal seeks: %llu\n", stat.total_seeks);
+ printf("\t\tForward seeks: %llu\n", stat.forward_seeks);
+ printf("\t\tBackward seeks: %llu\n", stat.backward_seeks);
+ printf("\t\tAvg seek len: %s\n", stat.total_seeks ?
+ pretty_size(stat.total_seek_len / stat.total_seeks) :
+ pretty_size(0));
+ print_seek_histogram(&stat);
+ printf("\tTotal clusters: %llu\n", stat.total_clusters);
+ printf("\t\tAvg cluster size: %s\n",
+ pretty_size((stat.total_cluster_size /
+ stat.total_clusters)));
+ printf("\t\tMin cluster size: %s\n",
+ pretty_size(stat.min_cluster_size));
+ printf("\t\tMax cluster size: %s\n",
+ pretty_size(stat.max_cluster_size));
+ printf("\tTotal disk spread: %s\n",
+ pretty_size(stat.highest_bytenr -
+ stat.lowest_bytenr));
+ printf("\tTotal read time: %d s %d us\n", (int)diff.tv_sec,
+ (int)diff.tv_usec);
+ printf("\tLevels: %d\n", level + 1);
+ }
+out:
+ while ((n = rb_first(&stat.seek_root)) != NULL) {
+ struct seek *seek = rb_entry(n, struct seek, n);
+ rb_erase(n, &stat.seek_root);
+ free(seek);
+ }
+
+ /*
+ * We only use path to save node data in iterating,
+ * without holding eb's ref_cnt in path.
+ * Don't use btrfs_free_path() here, it will free these
+ * eb again, and cause many problems, as negative ref_cnt
+ * or invalid memory access.
+ */
+ free(path);
+ return ret;
+}
+
+const char * const cmd_inspect_tree_stats_usage[] = {
+ "btrfs inspect-internal tree-stats [options] <device>",
+ "Print various stats for trees",
+ "-b raw numbers in bytes",
+ NULL
+};
+
+int cmd_inspect_tree_stats(int argc, char **argv)
+{
+ struct btrfs_key key;
+ struct btrfs_root *root;
+ int opt;
+ int ret = 0;
+
+ while ((opt = getopt(argc, argv, "vb")) != -1) {
+ switch (opt) {
+ case 'v':
+ verbose++;
+ break;
+ case 'b':
+ no_pretty = 1;
+ break;
+ default:
+ usage(cmd_inspect_tree_stats_usage);
+ }
+ }
+
+ if (check_argc_exact(argc - optind, 1)) {
+ usage(cmd_inspect_tree_stats_usage);
+ }
+
+ /*
+ if ((ret = check_mounted(argv[optind])) < 0) {
+ fprintf(stderr, "Could not check mount status: %d\n", ret);
+ if (ret == -EACCES)
+ fprintf(stderr, "Maybe you need to run as root?\n");
+ return ret;
+ } else if (ret) {
+ fprintf(stderr, "%s is currently mounted. Aborting.\n",
+ argv[optind]);
+ return -EBUSY;
+ }
+ */
+
+ root = open_ctree(argv[optind], 0, 0);
+ if (!root) {
+ fprintf(stderr, "Couldn't open ctree\n");
+ exit(1);
+ }
+
+ printf("Calculating size of root tree\n");
+ key.objectid = BTRFS_ROOT_TREE_OBJECTID;
+ ret = calc_root_size(root, &key, 0);
+ if (ret)
+ goto out;
+
+ printf("Calculating size of extent tree\n");
+ key.objectid = BTRFS_EXTENT_TREE_OBJECTID;
+ ret = calc_root_size(root, &key, 0);
+ if (ret)
+ goto out;
+
+ printf("Calculating size of csum tree\n");
+ key.objectid = BTRFS_CSUM_TREE_OBJECTID;
+ ret = calc_root_size(root, &key, 0);
+ if (ret)
+ goto out;
+
+ key.objectid = BTRFS_FS_TREE_OBJECTID;
+ key.offset = (u64)-1;
+ printf("Calculatin' size of fs tree\n");
+ ret = calc_root_size(root, &key, 1);
+ if (ret)
+ goto out;
+out:
+ close_ctree(root);
+ return ret;
+}
diff --git a/cmds-inspect-tree-stats.h b/cmds-inspect-tree-stats.h
new file mode 100644
index 00000000..eb401dac
--- /dev/null
+++ b/cmds-inspect-tree-stats.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2011 Red Hat. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#ifndef __CMDS_INSPECT_TREE_STATS_H__
+#define __CMDS_INSPECT_TREE_STATS_H__
+
+int cmd_inspect_tree_stats(int argc, char **argv);
+
+extern const char * const cmd_inspect_tree_stats_usage[];
+
+#endif
diff --git a/cmds-inspect.c b/cmds-inspect.c
index 7fa4881a..abe4edf1 100644
--- a/cmds-inspect.c
+++ b/cmds-inspect.c
@@ -31,6 +31,9 @@
#include "disk-io.h"
#include "commands.h"
#include "btrfs-list.h"
+#include "cmds-inspect-dump-tree.h"
+#include "cmds-inspect-dump-super.h"
+#include "cmds-inspect-tree-stats.h"
static const char * const inspect_cmd_group_usage[] = {
"btrfs inspect-internal <command> <args>",
@@ -51,7 +54,7 @@ static int __ino_to_path_fd(u64 inum, int fd, int verbose, const char *prepend)
ret = ioctl(fd, BTRFS_IOC_INO_PATHS, &ipa);
if (ret < 0) {
- printf("ioctl ret=%d, error: %s\n", ret, strerror(errno));
+ error("ino paths ioctl: %s", strerror(errno));
goto out;
}
@@ -190,7 +193,7 @@ static int cmd_inspect_logical_resolve(int argc, char **argv)
ret = ioctl(fd, BTRFS_IOC_LOGICAL_INO, &loi);
if (ret < 0) {
- printf("ioctl ret=%d, error: %s\n", ret, strerror(errno));
+ error("logical ino ioctl: %s", strerror(errno));
goto out;
}
@@ -266,22 +269,23 @@ static int cmd_inspect_subvolid_resolve(int argc, char **argv)
char path[PATH_MAX];
DIR *dirstream = NULL;
- if (check_argc_exact(argc, 3))
+ clean_args_no_options(argc, argv, cmd_inspect_subvolid_resolve_usage);
+
+ if (check_argc_exact(argc - optind, 2))
usage(cmd_inspect_subvolid_resolve_usage);
- fd = btrfs_open_dir(argv[2], &dirstream, 1);
+ fd = btrfs_open_dir(argv[optind + 1], &dirstream, 1);
if (fd < 0) {
ret = -ENOENT;
goto out;
}
- subvol_id = arg_strtou64(argv[1]);
+ subvol_id = arg_strtou64(argv[optind]);
ret = btrfs_subvolid_resolve(fd, path, sizeof(path), subvol_id);
if (ret) {
- fprintf(stderr,
- "%s: btrfs_subvolid_resolve(subvol_id %llu) failed with ret=%d\n",
- argv[0], (unsigned long long)subvol_id, ret);
+ error("resolving subvolid %llu error %d",
+ (unsigned long long)subvol_id, ret);
goto out;
}
@@ -290,7 +294,7 @@ static int cmd_inspect_subvolid_resolve(int argc, char **argv)
out:
close_file_or_dir(fd, dirstream);
- return ret ? 1 : 0;
+ return !!ret;
}
static const char* const cmd_inspect_rootid_usage[] = {
@@ -306,10 +310,12 @@ static int cmd_inspect_rootid(int argc, char **argv)
u64 rootid;
DIR *dirstream = NULL;
- if (check_argc_exact(argc, 2))
+ clean_args_no_options(argc, argv, cmd_inspect_rootid_usage);
+
+ if (check_argc_exact(argc - optind, 1))
usage(cmd_inspect_rootid_usage);
- fd = btrfs_open_dir(argv[1], &dirstream, 1);
+ fd = btrfs_open_dir(argv[optind], &dirstream, 1);
if (fd < 0) {
ret = -ENOENT;
goto out;
@@ -317,8 +323,7 @@ static int cmd_inspect_rootid(int argc, char **argv)
ret = lookup_ino_rootid(fd, &rootid);
if (ret) {
- fprintf(stderr, "%s: rootid failed with ret=%d\n",
- argv[0], ret);
+ error("rootid failed with ret=%d", ret);
goto out;
}
@@ -516,9 +521,7 @@ static int print_min_dev_size(int fd, u64 devid)
ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
if (ret < 0) {
- fprintf(stderr,
- "Error invoking tree search ioctl: %s\n",
- strerror(errno));
+ error("tree search ioctl: %s", strerror(errno));
ret = 1;
goto out;
}
@@ -554,7 +557,7 @@ static int print_min_dev_size(int fd, u64 devid)
ret = add_dev_extent(&holes, last_pos,
sh->offset - 1, 1);
if (ret) {
- fprintf(stderr, "Error: %s\n", strerror(-ret));
+ error("add device extent: %s", strerror(-ret));
ret = 1;
goto out;
}
@@ -634,6 +637,12 @@ const struct cmd_group inspect_cmd_group = {
0 },
{ "min-dev-size", cmd_inspect_min_dev_size,
cmd_inspect_min_dev_size_usage, NULL, 0 },
+ { "dump-tree", cmd_inspect_dump_tree,
+ cmd_inspect_dump_tree_usage, NULL, 0 },
+ { "dump-super", cmd_inspect_dump_super,
+ cmd_inspect_dump_super_usage, NULL, 0 },
+ { "tree-stats", cmd_inspect_tree_stats,
+ cmd_inspect_tree_stats_usage, NULL, 0 },
NULL_CMD_STRUCT
}
};
diff --git a/cmds-property.c b/cmds-property.c
index b7b24841..48a8945a 100644
--- a/cmds-property.c
+++ b/cmds-property.c
@@ -294,10 +294,11 @@ out:
static void parse_args(int argc, char **argv,
const char * const *usage_str,
int *types, char **object,
- char **name, char **value)
+ char **name, char **value, int min_nonopt_args)
{
int ret;
char *type_str = NULL;
+ int max_nonopt_args = 0;
optind = 1;
while (1) {
@@ -314,6 +315,17 @@ static void parse_args(int argc, char **argv,
}
}
+ if (object)
+ max_nonopt_args++;
+ if (name)
+ max_nonopt_args++;
+ if (value)
+ max_nonopt_args++;
+
+ if (check_argc_min(argc - optind, min_nonopt_args) ||
+ check_argc_max(argc - optind, max_nonopt_args))
+ usage(usage_str);
+
*types = 0;
if (type_str) {
if (!strcmp(type_str, "s") || !strcmp(type_str, "subvol")) {
@@ -340,11 +352,6 @@ static void parse_args(int argc, char **argv,
if (value && optind < argc)
*value = argv[optind++];
- if (optind != argc) {
- error("unexpected agruments found");
- usage(usage_str);
- }
-
if (!*types && object && *object) {
ret = autodetect_object_types(*object, types);
if (ret < 0) {
@@ -379,11 +386,8 @@ static int cmd_property_get(int argc, char **argv)
char *name = NULL;
int types = 0;
- if (check_argc_min(argc, 2) || check_argc_max(argc, 5))
- usage(cmd_property_get_usage);
-
parse_args(argc, argv, cmd_property_get_usage, &types, &object, &name,
- NULL);
+ NULL, 1);
if (!object) {
error("invalid arguments");
usage(cmd_property_get_usage);
@@ -413,11 +417,8 @@ static int cmd_property_set(int argc, char **argv)
char *value = NULL;
int types = 0;
- if (check_argc_min(argc, 4) || check_argc_max(argc, 6))
- usage(cmd_property_set_usage);
-
parse_args(argc, argv, cmd_property_set_usage, &types,
- &object, &name, &value);
+ &object, &name, &value, 3);
if (!object || !name || !value) {
error("invalid arguments");
usage(cmd_property_set_usage);
@@ -442,11 +443,8 @@ static int cmd_property_list(int argc, char **argv)
char *object = NULL;
int types = 0;
- if (check_argc_min(argc, 2) || check_argc_max(argc, 4))
- usage(cmd_property_list_usage);
-
parse_args(argc, argv, cmd_property_list_usage,
- &types, &object, NULL, NULL);
+ &types, &object, NULL, NULL, 1);
if (!object) {
error("invalid arguments");
usage(cmd_property_list_usage);
diff --git a/cmds-qgroup.c b/cmds-qgroup.c
index db5ee21a..14418d45 100644
--- a/cmds-qgroup.c
+++ b/cmds-qgroup.c
@@ -32,7 +32,8 @@ static const char * const qgroup_cmd_group_usage[] = {
NULL
};
-static int qgroup_assign(int assign, int argc, char **argv)
+static int _cmd_qgroup_assign(int assign, int argc, char **argv,
+ const char * const *usage_str)
{
int ret = 0;
int fd;
@@ -41,28 +42,38 @@ static int qgroup_assign(int assign, int argc, char **argv)
struct btrfs_ioctl_qgroup_assign_args args;
DIR *dirstream = NULL;
- while (1) {
- enum { GETOPT_VAL_RESCAN = 256 };
- static const struct option long_options[] = {
- { "rescan", no_argument, NULL, GETOPT_VAL_RESCAN },
- { NULL, 0, NULL, 0 }
- };
- int c = getopt_long(argc, argv, "", long_options, NULL);
-
- if (c < 0)
- break;
- switch (c) {
- case GETOPT_VAL_RESCAN:
- rescan = 1;
- break;
- default:
- /* Usage printed by the caller */
- return -1;
+ if (assign) {
+ while (1) {
+ enum { GETOPT_VAL_RESCAN = 256, GETOPT_VAL_NO_RESCAN };
+ static const struct option long_options[] = {
+ { "rescan", no_argument, NULL,
+ GETOPT_VAL_RESCAN },
+ { "no-rescan", no_argument, NULL,
+ GETOPT_VAL_NO_RESCAN },
+ { NULL, 0, NULL, 0 }
+ };
+ int c = getopt_long(argc, argv, "", long_options, NULL);
+
+ if (c < 0)
+ break;
+ switch (c) {
+ case GETOPT_VAL_RESCAN:
+ rescan = 1;
+ break;
+ case GETOPT_VAL_NO_RESCAN:
+ rescan = 0;
+ break;
+ default:
+ /* Usage printed by the caller */
+ return -1;
+ }
}
+ } else {
+ clean_args_no_options(argc, argv, usage_str);
}
if (check_argc_exact(argc - optind, 3))
- return -1;
+ usage(usage_str);
memset(&args, 0, sizeof(args));
args.assign = assign;
@@ -115,21 +126,22 @@ static int qgroup_assign(int assign, int argc, char **argv)
return ret;
}
-static int qgroup_create(int create, int argc, char **argv)
+static int _cmd_qgroup_create(int create, int argc, char **argv)
{
int ret = 0;
int fd;
int e;
- char *path = argv[2];
+ char *path;
struct btrfs_ioctl_qgroup_create_args args;
DIR *dirstream = NULL;
- if (check_argc_exact(argc, 3))
+ if (check_argc_exact(argc - optind, 2))
return -1;
memset(&args, 0, sizeof(args));
args.create = create;
- args.qgroupid = parse_qgroupid(argv[1]);
+ args.qgroupid = parse_qgroupid(argv[optind]);
+ path = argv[optind + 1];
fd = btrfs_open_dir(path, &dirstream, 1);
if (fd < 0)
@@ -201,16 +213,13 @@ static const char * const cmd_qgroup_assign_usage[] = {
"Assign SRC as the child qgroup of DST",
"",
"--rescan schedule qutoa rescan if needed",
- "--no-rescan ",
+ "--no-rescan don't schedule quota rescan",
NULL
};
static int cmd_qgroup_assign(int argc, char **argv)
{
- int ret = qgroup_assign(1, argc, argv);
- if (ret < 0)
- usage(cmd_qgroup_assign_usage);
- return ret;
+ return _cmd_qgroup_assign(1, argc, argv, cmd_qgroup_assign_usage);
}
static const char * const cmd_qgroup_remove_usage[] = {
@@ -221,10 +230,7 @@ static const char * const cmd_qgroup_remove_usage[] = {
static int cmd_qgroup_remove(int argc, char **argv)
{
- int ret = qgroup_assign(0, argc, argv);
- if (ret < 0)
- usage(cmd_qgroup_remove_usage);
- return ret;
+ return _cmd_qgroup_assign(0, argc, argv, cmd_qgroup_remove_usage);
}
static const char * const cmd_qgroup_create_usage[] = {
@@ -235,7 +241,12 @@ static const char * const cmd_qgroup_create_usage[] = {
static int cmd_qgroup_create(int argc, char **argv)
{
- int ret = qgroup_create(1, argc, argv);
+ int ret;
+
+ clean_args_no_options(argc, argv, cmd_qgroup_create_usage);
+
+ ret = _cmd_qgroup_create(1, argc, argv);
+
if (ret < 0)
usage(cmd_qgroup_create_usage);
return ret;
@@ -249,7 +260,12 @@ static const char * const cmd_qgroup_destroy_usage[] = {
static int cmd_qgroup_destroy(int argc, char **argv)
{
- int ret = qgroup_create(0, argc, argv);
+ int ret;
+
+ clean_args_no_options(argc, argv, cmd_qgroup_destroy_usage);
+
+ ret = _cmd_qgroup_create(0, argc, argv);
+
if (ret < 0)
usage(cmd_qgroup_destroy_usage);
return ret;
diff --git a/cmds-quota.c b/cmds-quota.c
index 34b8dacf..568482fc 100644
--- a/cmds-quota.c
+++ b/cmds-quota.c
@@ -70,7 +70,12 @@ static const char * const cmd_quota_enable_usage[] = {
static int cmd_quota_enable(int argc, char **argv)
{
- int ret = quota_ctl(BTRFS_QUOTA_CTL_ENABLE, argc, argv);
+ int ret;
+
+ clean_args_no_options(argc, argv, cmd_quota_enable_usage);
+
+ ret = quota_ctl(BTRFS_QUOTA_CTL_ENABLE, argc, argv);
+
if (ret < 0)
usage(cmd_quota_enable_usage);
return ret;
@@ -84,7 +89,12 @@ static const char * const cmd_quota_disable_usage[] = {
static int cmd_quota_disable(int argc, char **argv)
{
- int ret = quota_ctl(BTRFS_QUOTA_CTL_DISABLE, argc, argv);
+ int ret;
+
+ clean_args_no_options(argc, argv, cmd_quota_disable_usage);
+
+ ret = quota_ctl(BTRFS_QUOTA_CTL_DISABLE, argc, argv);
+
if (ret < 0)
usage(cmd_quota_disable_usage);
return ret;
diff --git a/cmds-rescue.c b/cmds-rescue.c
index 98954254..f597997b 100644
--- a/cmds-rescue.c
+++ b/cmds-rescue.c
@@ -67,8 +67,7 @@ static int cmd_rescue_chunk_recover(int argc, char *argv[])
}
}
- argc = argc - optind;
- if (check_argc_exact(argc, 1))
+ if (check_argc_exact(argc - optind, 1))
usage(cmd_rescue_chunk_recover_usage);
file = argv[optind];
@@ -133,8 +132,7 @@ static int cmd_rescue_super_recover(int argc, char **argv)
usage(cmd_rescue_super_recover_usage);
}
}
- argc = argc - optind;
- if (check_argc_exact(argc, 1))
+ if (check_argc_exact(argc - optind, 1))
usage(cmd_rescue_super_recover_usage);
dname = argv[optind];
@@ -165,6 +163,8 @@ static int cmd_rescue_zero_log(int argc, char **argv)
char *devname;
int ret;
+ clean_args_no_options(argc, argv, cmd_rescue_zero_log_usage);
+
if (check_argc_exact(argc, 2))
usage(cmd_rescue_zero_log_usage);
diff --git a/cmds-restore.c b/cmds-restore.c
index dd0b2427..b491f083 100644
--- a/cmds-restore.c
+++ b/cmds-restore.c
@@ -56,7 +56,6 @@ static int get_xattrs = 0;
static int dry_run = 0;
#define LZO_LEN 4
-#define PAGE_CACHE_SIZE 4096
#define lzo1x_worst_compress(x) ((x) + ((x) / 16) + 64 + 3)
static int decompress_zlib(char *inbuf, char *outbuf, u64 compress_len,
@@ -68,7 +67,7 @@ static int decompress_zlib(char *inbuf, char *outbuf, u64 compress_len,
memset(&strm, 0, sizeof(strm));
ret = inflateInit(&strm);
if (ret != Z_OK) {
- fprintf(stderr, "inflate init returnd %d\n", ret);
+ error("zlib init returned %d", ret);
return -1;
}
@@ -79,7 +78,7 @@ static int decompress_zlib(char *inbuf, char *outbuf, u64 compress_len,
ret = inflate(&strm, Z_NO_FLUSH);
if (ret != Z_STREAM_END) {
(void)inflateEnd(&strm);
- fprintf(stderr, "failed to inflate: %d\n", ret);
+ error("zlib inflate failed: %d", ret);
return -1;
}
@@ -93,8 +92,8 @@ static inline size_t read_compress_length(unsigned char *buf)
return le32_to_cpu(dlen);
}
-static int decompress_lzo(unsigned char *inbuf, char *outbuf, u64 compress_len,
- u64 *decompress_len)
+static int decompress_lzo(struct btrfs_root *root, unsigned char *inbuf,
+ char *outbuf, u64 compress_len, u64 *decompress_len)
{
size_t new_len;
size_t in_len;
@@ -105,7 +104,7 @@ static int decompress_lzo(unsigned char *inbuf, char *outbuf, u64 compress_len,
ret = lzo_init();
if (ret != LZO_E_OK) {
- fprintf(stderr, "lzo init returned %d\n", ret);
+ error("lzo init returned %d", ret);
return -1;
}
@@ -119,20 +118,19 @@ static int decompress_lzo(unsigned char *inbuf, char *outbuf, u64 compress_len,
in_len = read_compress_length(inbuf);
if ((tot_in + LZO_LEN + in_len) > tot_len) {
- fprintf(stderr, "bad compress length %lu\n",
+ error("bad compress length %lu",
(unsigned long)in_len);
return -1;
}
inbuf += LZO_LEN;
tot_in += LZO_LEN;
-
- new_len = lzo1x_worst_compress(PAGE_CACHE_SIZE);
+ new_len = lzo1x_worst_compress(root->sectorsize);
ret = lzo1x_decompress_safe((const unsigned char *)inbuf, in_len,
(unsigned char *)outbuf,
(void *)&new_len, NULL);
if (ret != LZO_E_OK) {
- fprintf(stderr, "failed to inflate: %d\n", ret);
+ error("lzo decompress failed: %d", ret);
return -1;
}
out_len += new_len;
@@ -144,8 +142,8 @@ static int decompress_lzo(unsigned char *inbuf, char *outbuf, u64 compress_len,
* 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 % PAGE_CACHE_SIZE;
- rem_page = PAGE_CACHE_SIZE - mod_page;
+ mod_page = tot_in % root->sectorsize;
+ rem_page = root->sectorsize - mod_page;
if (rem_page < LZO_LEN) {
inbuf += rem_page;
tot_in += rem_page;
@@ -157,21 +155,21 @@ static int decompress_lzo(unsigned char *inbuf, char *outbuf, u64 compress_len,
return 0;
}
-static int decompress(char *inbuf, char *outbuf, u64 compress_len,
- u64 *decompress_len, int compress)
+static int decompress(struct btrfs_root *root, char *inbuf, char *outbuf,
+ u64 compress_len, u64 *decompress_len, int compress)
{
switch (compress) {
case BTRFS_COMPRESS_ZLIB:
return decompress_zlib(inbuf, outbuf, compress_len,
*decompress_len);
case BTRFS_COMPRESS_LZO:
- return decompress_lzo((unsigned char *)inbuf, outbuf, compress_len,
- decompress_len);
+ return decompress_lzo(root, (unsigned char *)inbuf, outbuf,
+ compress_len, decompress_len);
default:
break;
}
- fprintf(stderr, "invalid compression type: %d\n", compress);
+ error("invalid compression type: %d", compress);
return -1;
}
@@ -234,7 +232,8 @@ again:
return 0;
}
-static int copy_one_inline(int fd, struct btrfs_path *path, u64 pos)
+static int copy_one_inline(struct btrfs_root *root, int fd,
+ struct btrfs_path *path, u64 pos)
{
struct extent_buffer *leaf = path->nodes[0];
struct btrfs_file_extent_item *fi;
@@ -269,11 +268,11 @@ static int copy_one_inline(int fd, struct btrfs_path *path, u64 pos)
ram_size = btrfs_file_extent_ram_bytes(leaf, fi);
outbuf = calloc(1, ram_size);
if (!outbuf) {
- fprintf(stderr, "No memory\n");
+ error("not enough memory");
return -ENOMEM;
}
- ret = decompress(buf, outbuf, len, &ram_size, compress);
+ ret = decompress(root, buf, outbuf, len, &ram_size, compress);
if (ret) {
free(outbuf);
return ret;
@@ -331,14 +330,14 @@ static int copy_one_extent(struct btrfs_root *root, int fd,
inbuf = malloc(size_left);
if (!inbuf) {
- fprintf(stderr, "No memory\n");
+ error("not enough memory\n");
return -ENOMEM;
}
if (compress != BTRFS_COMPRESS_NONE) {
outbuf = calloc(1, ram_size);
if (!outbuf) {
- fprintf(stderr, "No memory\n");
+ error("not enough memory");
free(inbuf);
return -ENOMEM;
}
@@ -348,7 +347,9 @@ again:
ret = btrfs_map_block(&root->fs_info->mapping_tree, READ,
bytenr, &length, &multi, mirror_num, NULL);
if (ret) {
- fprintf(stderr, "Error mapping block %d\n", ret);
+ error("cannot map block logical %llu length %llu: %d",
+ (unsigned long long)bytenr,
+ (unsigned long long)length, ret);
goto out;
}
device = multi->stripes[0].dev;
@@ -369,7 +370,8 @@ again:
/* mirror_num is 1-indexed, so num_copies is a valid mirror. */
if (mirror_num > num_copies) {
ret = -1;
- fprintf(stderr, "Exhausted mirrors trying to read\n");
+ error("exhausted mirrors trying to read (%d > %d)",
+ mirror_num, num_copies);
goto out;
}
fprintf(stderr, "Trying another mirror\n");
@@ -389,7 +391,7 @@ again:
pos+total);
if (done < 0) {
ret = -1;
- fprintf(stderr, "Error writing: %d %s\n", errno, strerror(errno));
+ error("cannot write data: %d %s", errno, strerror(errno));
goto out;
}
total += done;
@@ -398,7 +400,7 @@ again:
goto out;
}
- ret = decompress(inbuf, outbuf, disk_size, &ram_size, compress);
+ ret = decompress(root, inbuf, outbuf, disk_size, &ram_size, compress);
if (ret) {
num_copies = btrfs_num_copies(&root->fs_info->mapping_tree,
bytenr, length);
@@ -488,8 +490,7 @@ static int set_file_xattrs(struct btrfs_root *root, u64 inode,
do {
ret = next_leaf(root, path);
if (ret < 0) {
- fprintf(stderr,
- "Error searching for extended attributes: %d\n",
+ error("searching for extended attributes: %d\n",
ret);
goto out;
} else if (ret) {
@@ -540,8 +541,7 @@ static int set_file_xattrs(struct btrfs_root *root, u64 inode,
data_len = len;
if (fsetxattr(fd, name, data, data_len, 0))
- fprintf(stderr,
- "Error setting extended attribute %s on file %s: %s\n",
+ error("setting extended attribute %s on file %s: %s",
name, file_name, strerror(errno));
len = sizeof(*di) + name_len + data_len;
@@ -568,7 +568,7 @@ static int copy_metadata(struct btrfs_root *root, int fd,
path = btrfs_alloc_path();
if (!path) {
- fprintf(stderr, "ERROR: Ran out of memory\n");
+ error("not enough memory");
return -ENOMEM;
}
@@ -583,15 +583,13 @@ static int copy_metadata(struct btrfs_root *root, int fd,
ret = fchown(fd, btrfs_inode_uid(path->nodes[0], inode_item),
btrfs_inode_gid(path->nodes[0], inode_item));
if (ret) {
- fprintf(stderr, "ERROR: Failed to change owner: %s\n",
- strerror(errno));
+ error("failed to change owner: %s", strerror(errno));
goto out;
}
ret = fchmod(fd, btrfs_inode_mode(path->nodes[0], inode_item));
if (ret) {
- fprintf(stderr, "ERROR: Failed to change mode: %s\n",
- strerror(errno));
+ error("failed to change mode: %s", strerror(errno));
goto out;
}
@@ -605,8 +603,7 @@ static int copy_metadata(struct btrfs_root *root, int fd,
ret = futimens(fd, times);
if (ret) {
- fprintf(stderr, "ERROR: Failed to set times: %s\n",
- strerror(errno));
+ error("failed to set times: %s", strerror(errno));
goto out;
}
}
@@ -634,7 +631,7 @@ static int copy_file(struct btrfs_root *root, int fd, struct btrfs_key *key,
path = btrfs_alloc_path();
if (!path) {
- fprintf(stderr, "Ran out of memory\n");
+ error("not enough memory");
return -ENOMEM;
}
@@ -676,7 +673,7 @@ static int copy_file(struct btrfs_root *root, int fd, struct btrfs_key *key,
ret = btrfs_search_slot(NULL, root, key, path, 0, 0);
if (ret < 0) {
- fprintf(stderr, "Error searching %d\n", ret);
+ error("searching extent data returned %d", ret);
goto out;
}
@@ -684,8 +681,7 @@ static int copy_file(struct btrfs_root *root, int fd, struct btrfs_key *key,
while (!leaf) {
ret = next_leaf(root, path);
if (ret < 0) {
- fprintf(stderr, "Error getting next leaf %d\n",
- ret);
+ error("cannot get next leaf: %d", ret);
goto out;
} else if (ret > 0) {
/* No more leaves to search */
@@ -732,7 +728,7 @@ static int copy_file(struct btrfs_root *root, int fd, struct btrfs_key *key,
extent_type = btrfs_file_extent_type(leaf, fi);
compression = btrfs_file_extent_compression(leaf, fi);
if (compression >= BTRFS_COMPRESS_LAST) {
- fprintf(stderr, "Don't support compression yet %d\n",
+ warning("compression type %d not supported",
compression);
ret = -1;
goto out;
@@ -741,7 +737,7 @@ static int copy_file(struct btrfs_root *root, int fd, struct btrfs_key *key,
if (extent_type == BTRFS_FILE_EXTENT_PREALLOC)
goto next;
if (extent_type == BTRFS_FILE_EXTENT_INLINE) {
- ret = copy_one_inline(fd, path, found_key.offset);
+ ret = copy_one_inline(root, fd, path, found_key.offset);
if (ret)
goto out;
} else if (extent_type == BTRFS_FILE_EXTENT_REG) {
@@ -750,7 +746,7 @@ static int copy_file(struct btrfs_root *root, int fd, struct btrfs_key *key,
if (ret)
goto out;
} else {
- printf("Weird extent type %d\n", extent_type);
+ warning("weird extent type %d", extent_type);
}
next:
path->slots[0]++;
@@ -1265,7 +1261,7 @@ static struct btrfs_root *open_fs(const char *dev, u64 root_location,
for (i = super_mirror; i < BTRFS_SUPER_MIRROR_MAX; i++) {
bytenr = btrfs_sb_offset(i);
- fs_info = open_ctree_fs_info(dev, bytenr, root_location,
+ fs_info = open_ctree_fs_info(dev, bytenr, root_location, 0,
OPEN_CTREE_PARTIAL);
if (fs_info)
break;
@@ -1288,7 +1284,7 @@ static struct btrfs_root *open_fs(const char *dev, u64 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->leafsize, generation);
+ root->nodesize, generation);
if (!extent_buffer_uptodate(root->node)) {
fprintf(stderr, "Error opening tree root\n");
close_ctree(root);
@@ -1539,7 +1535,7 @@ 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->leafsize, 0);
+ root->node = read_tree_block(root, fs_location, root->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 da614f2f..de7005c1 100644
--- a/cmds-scrub.c
+++ b/cmds-scrub.c
@@ -1591,10 +1591,12 @@ static int cmd_scrub_cancel(int argc, char **argv)
int fdmnt = -1;
DIR *dirstream = NULL;
- if (check_argc_exact(argc, 2))
+ clean_args_no_options(argc, argv, cmd_scrub_cancel_usage);
+
+ if (check_argc_exact(argc - optind, 1))
usage(cmd_scrub_cancel_usage);
- path = argv[1];
+ path = argv[optind];
fdmnt = open_path_or_dev_mnt(path, &dirstream, 1);
if (fdmnt < 0) {
diff --git a/cmds-send.c b/cmds-send.c
index 3e34d75b..4063475a 100644
--- a/cmds-send.c
+++ b/cmds-send.c
@@ -335,17 +335,6 @@ out:
return ret;
}
-char *get_subvol_name(char *mnt, char *full_path)
-{
- int len = strlen(mnt);
- if (!len)
- return full_path;
- if (mnt[len - 1] != '/')
- len += 1;
-
- return full_path + len;
-}
-
static int init_root_path(struct btrfs_send *s, const char *subvol)
{
int ret = 0;
@@ -466,8 +455,9 @@ int cmd_send(int argc, char **argv)
if (ret < 0)
goto out;
- ret = get_root_id(&send, get_subvol_name(send.root_path, subvol),
- &root_id);
+ ret = get_root_id(&send,
+ subvol_strip_mountpoint(send.root_path, subvol),
+ &root_id);
if (ret < 0) {
error("cannot resolve rootid for %s", subvol);
goto out;
@@ -484,7 +474,7 @@ int cmd_send(int argc, char **argv)
ret = add_clone_source(&send, root_id);
if (ret < 0) {
- error("not enough memory");
+ error("cannot add clone source: %s", strerror(-ret));
goto out;
}
subvol_uuid_search_finit(&send.sus);
@@ -580,8 +570,8 @@ int cmd_send(int argc, char **argv)
if (snapshot_parent != NULL) {
ret = get_root_id(&send,
- get_subvol_name(send.root_path, snapshot_parent),
- &parent_root_id);
+ subvol_strip_mountpoint(send.root_path, snapshot_parent),
+ &parent_root_id);
if (ret < 0) {
error("could not resolve rootid for %s", snapshot_parent);
goto out;
@@ -589,7 +579,7 @@ int cmd_send(int argc, char **argv)
ret = add_clone_source(&send, parent_root_id);
if (ret < 0) {
- error("not enough memory");
+ error("cannot add clone source: %s", strerror(-ret));
goto out;
}
}
@@ -683,15 +673,16 @@ int cmd_send(int argc, char **argv)
if (ret < 0)
goto out;
- /* done with this subvol, so add it to the clone sources */
- ret = add_clone_source(&send, root_id);
- if (ret < 0) {
- error("not enough memory");
- goto out;
+ if (!full_send) {
+ /* done with this subvol, so add it to the clone sources */
+ ret = add_clone_source(&send, root_id);
+ if (ret < 0) {
+ error("cannot add clone source: %s", strerror(-ret));
+ goto out;
+ }
}
parent_root_id = 0;
- full_send = 0;
}
ret = 0;
diff --git a/cmds-subvolume.c b/cmds-subvolume.c
index 9d9b0af7..2319684e 100644
--- a/cmds-subvolume.c
+++ b/cmds-subvolume.c
@@ -224,33 +224,6 @@ out:
return retval;
}
-/*
- * Test if path is a subvolume
- * Returns:
- * 0 - path exists but it is not a subvolume
- * 1 - path exists and it is a subvolume
- * < 0 - error
- */
-int test_issubvolume(const char *path)
-{
- struct stat st;
- struct statfs stfs;
- int res;
-
- res = stat(path, &st);
- if (res < 0)
- return -errno;
-
- if (st.st_ino != BTRFS_FIRST_FREE_OBJECTID || !S_ISDIR(st.st_mode))
- return 0;
-
- res = statfs(path, &stfs);
- if (res < 0)
- return -errno;
-
- return (int)stfs.f_type == BTRFS_SUPER_MAGIC;
-}
-
static int wait_for_commit(int fd)
{
int ret;
@@ -788,7 +761,9 @@ static int cmd_subvol_get_default(int argc, char **argv)
u64 default_id;
DIR *dirstream = NULL;
- if (check_argc_exact(argc, 2))
+ clean_args_no_options(argc, argv, cmd_subvol_get_default_usage);
+
+ if (check_argc_exact(argc - optind, 1))
usage(cmd_subvol_get_default_usage);
subvol = argv[1];
@@ -850,11 +825,13 @@ static int cmd_subvol_set_default(int argc, char **argv)
char *subvolid;
DIR *dirstream = NULL;
- if (check_argc_exact(argc, 3))
+ clean_args_no_options(argc, argv, cmd_subvol_set_default_usage);
+
+ if (check_argc_exact(argc - optind, 2))
usage(cmd_subvol_set_default_usage);
- subvolid = argv[1];
- path = argv[2];
+ subvolid = argv[optind];
+ path = argv[optind + 1];
objectid = arg_strtou64(subvolid);
@@ -887,11 +864,13 @@ static int cmd_subvol_find_new(int argc, char **argv)
u64 last_gen;
DIR *dirstream = NULL;
- if (check_argc_exact(argc, 3))
+ clean_args_no_options(argc, argv, cmd_subvol_find_new_usage);
+
+ if (check_argc_exact(argc - optind, 2))
usage(cmd_subvol_find_new_usage);
- subvol = argv[1];
- last_gen = arg_strtou64(argv[2]);
+ subvol = argv[optind];
+ last_gen = arg_strtou64(argv[optind + 1]);
ret = test_issubvolume(subvol);
if (ret < 0) {
@@ -929,21 +908,21 @@ static const char * const cmd_subvol_show_usage[] = {
static int cmd_subvol_show(int argc, char **argv)
{
struct root_info get_ri;
- struct btrfs_list_filter_set *filter_set;
+ struct btrfs_list_filter_set *filter_set = NULL;
char tstr[256];
char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
- char *fullpath = NULL, *svpath = NULL, *mnt = NULL;
+ char *fullpath = NULL;
char raw_prefix[] = "\t\t\t\t";
- u64 sv_id;
- int fd = -1, mntfd = -1;
+ int fd = -1;
int ret = 1;
- DIR *dirstream1 = NULL, *dirstream2 = NULL;
+ DIR *dirstream1 = NULL;
clean_args_no_options(argc, argv, cmd_subvol_show_usage);
if (check_argc_exact(argc - optind, 1))
usage(cmd_subvol_show_usage);
+ memset(&get_ri, 0, sizeof(get_ri));
fullpath = realpath(argv[optind], NULL);
if (!fullpath) {
error("cannot find real path for '%s': %s",
@@ -951,57 +930,23 @@ static int cmd_subvol_show(int argc, char **argv)
goto out;
}
- ret = test_issubvolume(fullpath);
- if (ret < 0) {
- error("cannot access subvolume %s: %s", fullpath,
- strerror(-ret));
- goto out;
- }
- if (!ret) {
- error("not a subvolume: %s", fullpath);
- ret = 1;
- goto out;
- }
-
- ret = find_mount_root(fullpath, &mnt);
- if (ret < 0) {
- error("find_mount_root failed on '%s': %s",
- fullpath, strerror(-ret));
- goto out;
- }
- if (ret > 0) {
- error("%s doesn't belong to btrfs mount point", fullpath);
- goto out;
- }
- ret = 1;
- svpath = get_subvol_name(mnt, fullpath);
-
- fd = btrfs_open_dir(fullpath, &dirstream1, 1);
- if (fd < 0)
- goto out;
-
- ret = btrfs_list_get_path_rootid(fd, &sv_id);
- if (ret) {
- error("can't get rootid for '%s'", fullpath);
- goto out;
- }
-
- mntfd = btrfs_open_dir(mnt, &dirstream2, 1);
- if (mntfd < 0)
- goto out;
-
- if (sv_id == BTRFS_FS_TREE_OBJECTID) {
+ ret = get_subvol_info(fullpath, &get_ri);
+ if (ret == 2) {
+ /*
+ * Since the top level btrfs was given don't
+ * take that as error
+ */
printf("%s is toplevel subvolume\n", fullpath);
+ ret = 0;
goto out;
}
-
- memset(&get_ri, 0, sizeof(get_ri));
- get_ri.root_id = sv_id;
-
- ret = btrfs_get_subvol(mntfd, &get_ri);
if (ret) {
- error("can't find '%s'", svpath);
- goto out;
+ ret < 0 ?
+ error("Failed to get subvol info %s: %s\n",
+ fullpath, strerror(-ret)):
+ error("Failed to get subvol info %s: %d\n",
+ fullpath, ret);
+ return ret;
}
/* print the info */
@@ -1052,19 +997,23 @@ static int cmd_subvol_show(int argc, char **argv)
btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT,
(u64)(unsigned long)get_ri.uuid);
btrfs_list_setup_print_column(BTRFS_LIST_PATH);
+
+ fd = open_file_or_dir(fullpath, &dirstream1);
+ if (fd < 0) {
+ fprintf(stderr, "ERROR: can't access '%s'\n", fullpath);
+ goto out;
+ }
btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
1, raw_prefix);
+out:
/* clean up */
free(get_ri.path);
free(get_ri.name);
free(get_ri.full_path);
btrfs_list_free_filter_set(filter_set);
-out:
close_file_or_dir(fd, dirstream1);
- close_file_or_dir(mntfd, dirstream2);
- free(mnt);
free(fullpath);
return !!ret;
}
@@ -1198,7 +1147,8 @@ static int enumerate_dead_subvols(int fd, u64 **ids)
u64 *newids;
count += SUBVOL_ID_BATCH;
- newids = (u64*)realloc(*ids, count);
+ newids = (u64*)realloc(*ids,
+ count * sizeof(u64));
if (!newids)
return -ENOMEM;
*ids = newids;
diff --git a/configure b/configure
index 552e186d..7d51d42b 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.4.1.
+# Generated by GNU Autoconf 2.69 for btrfs-progs v4.5.2.
#
# 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.4.1'
-PACKAGE_STRING='btrfs-progs v4.4.1'
+PACKAGE_VERSION='v4.5.2'
+PACKAGE_STRING='btrfs-progs v4.5.2'
PACKAGE_BUGREPORT='linux-btrfs@vger.kernel.org'
PACKAGE_URL='http://btrfs.wiki.kernel.org'
@@ -1287,7 +1287,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.4.1 to adapt to many kinds of systems.
+\`configure' configures btrfs-progs v4.5.2 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1352,7 +1352,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of btrfs-progs v4.4.1:";;
+ short | recursive ) echo "Configuration of btrfs-progs v4.5.2:";;
esac
cat <<\_ACEOF
@@ -1461,7 +1461,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
-btrfs-progs configure v4.4.1
+btrfs-progs configure v4.5.2
generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc.
@@ -1830,7 +1830,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.4.1, which was
+It was created by btrfs-progs $as_me v4.5.2, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
@@ -5369,12 +5369,12 @@ if test -n "$EXT2FS_CFLAGS"; then
pkg_cv_EXT2FS_CFLAGS="$EXT2FS_CFLAGS"
elif test -n "$PKG_CONFIG"; then
if test -n "$PKG_CONFIG" && \
- { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"ext2fs\""; } >&5
- ($PKG_CONFIG --exists --print-errors "ext2fs") 2>&5
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"ext2fs >= 1.41\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "ext2fs >= 1.41") 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
- pkg_cv_EXT2FS_CFLAGS=`$PKG_CONFIG --cflags "ext2fs" 2>/dev/null`
+ pkg_cv_EXT2FS_CFLAGS=`$PKG_CONFIG --cflags "ext2fs >= 1.41" 2>/dev/null`
test "x$?" != "x0" && pkg_failed=yes
else
pkg_failed=yes
@@ -5386,12 +5386,12 @@ if test -n "$EXT2FS_LIBS"; then
pkg_cv_EXT2FS_LIBS="$EXT2FS_LIBS"
elif test -n "$PKG_CONFIG"; then
if test -n "$PKG_CONFIG" && \
- { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"ext2fs\""; } >&5
- ($PKG_CONFIG --exists --print-errors "ext2fs") 2>&5
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"ext2fs >= 1.41\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "ext2fs >= 1.41") 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
- pkg_cv_EXT2FS_LIBS=`$PKG_CONFIG --libs "ext2fs" 2>/dev/null`
+ pkg_cv_EXT2FS_LIBS=`$PKG_CONFIG --libs "ext2fs >= 1.41" 2>/dev/null`
test "x$?" != "x0" && pkg_failed=yes
else
pkg_failed=yes
@@ -5412,14 +5412,14 @@ else
_pkg_short_errors_supported=no
fi
if test $_pkg_short_errors_supported = yes; then
- EXT2FS_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "ext2fs" 2>&1`
+ EXT2FS_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "ext2fs >= 1.41" 2>&1`
else
- EXT2FS_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "ext2fs" 2>&1`
+ EXT2FS_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "ext2fs >= 1.41" 2>&1`
fi
# Put the nasty error message in config.log where it belongs
echo "$EXT2FS_PKG_ERRORS" >&5
- as_fn_error $? "Package requirements (ext2fs) were not met:
+ as_fn_error $? "Package requirements (ext2fs >= 1.41) were not met:
$EXT2FS_PKG_ERRORS
@@ -6427,7 +6427,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.4.1, which was
+This file was extended by btrfs-progs $as_me v4.5.2, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -6490,7 +6490,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.4.1
+btrfs-progs config.status v4.5.2
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
diff --git a/configure.ac b/configure.ac
index fc343ea1..797eb790 100644
--- a/configure.ac
+++ b/configure.ac
@@ -105,7 +105,7 @@ AS_IF([test "x$enable_convert" = xyes], [DISABLE_BTRFSCONVERT=0], [DISABLE_BTRFS
AC_SUBST([DISABLE_BTRFSCONVERT])
if test "x$enable_convert" = xyes; then
- PKG_CHECK_MODULES(EXT2FS, [ext2fs])
+ PKG_CHECK_MODULES(EXT2FS, [ext2fs >= 1.41])
PKG_CHECK_MODULES(COM_ERR, [com_err])
fi
diff --git a/ctree.c b/ctree.c
index 04cc476e..c60f609d 100644
--- a/ctree.c
+++ b/ctree.c
@@ -20,6 +20,7 @@
#include "transaction.h"
#include "print-tree.h"
#include "repair.h"
+#include "internal.h"
static int split_node(struct btrfs_trans_handle *trans, struct btrfs_root
*root, struct btrfs_path *path, int level);
@@ -648,7 +649,7 @@ struct extent_buffer *read_node_slot(struct btrfs_root *root,
return NULL;
return read_tree_block(root, btrfs_node_blockptr(parent, slot),
- btrfs_level_size(root, level - 1),
+ root->nodesize,
btrfs_node_ptr_generation(parent, slot));
}
@@ -989,7 +990,7 @@ void reada_for_search(struct btrfs_root *root, struct btrfs_path *path,
node = path->nodes[level];
search = btrfs_node_blockptr(node, slot);
- blocksize = btrfs_level_size(root, level - 1);
+ blocksize = root->nodesize;
eb = btrfs_find_tree_block(root, search, blocksize);
if (eb) {
free_extent_buffer(eb);
@@ -2112,7 +2113,7 @@ again:
else
btrfs_item_key(l, &disk_key, mid);
- right = btrfs_alloc_free_block(trans, root, root->leafsize,
+ right = btrfs_alloc_free_block(trans, root, root->nodesize,
root->root_key.objectid,
&disk_key, 0, l->start, 0);
if (IS_ERR(right)) {
diff --git a/ctree.h b/ctree.h
index 5ab0f4a4..2db5c87c 100644
--- a/ctree.h
+++ b/ctree.h
@@ -345,7 +345,7 @@ struct btrfs_header {
sizeof(struct btrfs_header)) / \
sizeof(struct btrfs_key_ptr))
#define __BTRFS_LEAF_DATA_SIZE(bs) ((bs) - sizeof(struct btrfs_header))
-#define BTRFS_LEAF_DATA_SIZE(r) (__BTRFS_LEAF_DATA_SIZE(r->leafsize))
+#define BTRFS_LEAF_DATA_SIZE(r) (__BTRFS_LEAF_DATA_SIZE(r->nodesize))
#define BTRFS_MAX_INLINE_DATA_SIZE(r) (BTRFS_LEAF_DATA_SIZE(r) - \
sizeof(struct btrfs_item) - \
sizeof(struct btrfs_file_extent_item))
@@ -428,6 +428,7 @@ struct btrfs_super_block {
__le64 num_devices;
__le32 sectorsize;
__le32 nodesize;
+ /* Unused and must be equal to nodesize */
__le32 leafsize;
__le32 stripesize;
__le32 sys_chunk_array_size;
@@ -1060,10 +1061,10 @@ struct btrfs_root {
/* node allocations are done in nodesize units */
u32 nodesize;
- /* leaf allocations are done in leafsize units */
+ /* Unused, equal to nodesize */
u32 leafsize;
- /* leaf allocations are done in leafsize units */
+ /* leaf allocations are done in nodesize units */
u32 stripesize;
int ref_cows;
@@ -2222,6 +2223,11 @@ 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;
diff --git a/debian/changelog b/debian/changelog
index 290e1c44..aac54d2f 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,19 @@
+btrfs-progs (4.5.2-1) unstable; urgency=medium
+
+ * Thanks for NMU of package rename.
+ * New upstream release 4.5.2.
+ * Upload using dgit.
+ * Source-only upload.
+ * btrfs-convert should not be included in the initramfs, but should be
+ compiled. Using btrfs-convert is not a trivial operation, and
+ especially not from a minimal shell. Also it is known to fail, and for
+ a sophisticated user it is trivial to include it in the
+ initramfs. Thus won't fix Closes: #801192
+ * No sponsorship required Closes: #823474
+ * Add Provides btrfs-tools-udeb to the -progs-udeb package.
+
+ -- Dimitri John Ledkov <xnox@debian.org> Tue, 10 May 2016 09:59:51 +0100
+
btrfs-progs (4.4.1-1.1) unstable; urgency=medium
* Non-maintainer upload.
diff --git a/debian/control b/debian/control
index fc8c9489..1c1647f5 100644
--- a/debian/control
+++ b/debian/control
@@ -12,7 +12,7 @@ Build-Depends: debhelper (>= 9),
zlib1g-dev,
asciidoc,
xmlto,
-Standards-Version: 3.9.7
+Standards-Version: 3.9.8
Homepage: http://btrfs.wiki.kernel.org/
Package: btrfs-progs
@@ -40,6 +40,7 @@ Package-Type: udeb
Section: debian-installer
Architecture: linux-any
Depends: ${misc:Depends}, ${shlibs:Depends}
+Provides: btrfs-tools-udeb
Description: Checksumming Copy on Write Filesystem utilities (udeb)
Package: btrfs-progs-dbg
diff --git a/disk-io.c b/disk-io.c
index e520d80d..f1d46974 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 leafsize = btrfs_super_leafsize(fs_info->super_copy);
+ u32 nodesize = btrfs_super_nodesize(fs_info->super_copy);
int ret = BTRFS_BAD_FSID;
if (buf->start != btrfs_header_bytenr(buf))
@@ -64,7 +64,7 @@ static int check_tree_block(struct btrfs_fs_info *fs_info,
if (btrfs_header_level(buf) >= BTRFS_MAX_LEVEL)
return BTRFS_BAD_LEVEL;
if (btrfs_header_nritems(buf) > max_nritems(btrfs_header_level(buf),
- leafsize))
+ nodesize))
return BTRFS_BAD_NRITEMS;
fs_devices = fs_info->fs_devices;
@@ -619,7 +619,7 @@ static int find_and_setup_root(struct btrfs_root *tree_root,
if (ret)
return ret;
- blocksize = btrfs_level_size(root, btrfs_root_level(&root->root_item));
+ blocksize = root->nodesize;
generation = btrfs_root_generation(&root->root_item);
root->node = read_tree_block(root, btrfs_root_bytenr(&root->root_item),
blocksize, generation);
@@ -645,8 +645,7 @@ static int find_and_setup_log_root(struct btrfs_root *tree_root,
return 0;
}
- blocksize = btrfs_level_size(tree_root,
- btrfs_super_log_root_level(disk_super));
+ blocksize = tree_root->nodesize;
__setup_root(tree_root->nodesize, tree_root->leafsize,
tree_root->sectorsize, tree_root->stripesize,
@@ -737,7 +736,7 @@ out:
return ERR_PTR(ret);
}
generation = btrfs_root_generation(&root->root_item);
- blocksize = btrfs_level_size(root, btrfs_root_level(&root->root_item));
+ blocksize = root->nodesize;
root->node = read_tree_block(root, btrfs_root_bytenr(&root->root_item),
blocksize, generation);
if (!extent_buffer_uptodate(root->node)) {
@@ -934,7 +933,7 @@ static int setup_root_or_create_block(struct btrfs_fs_info *fs_info,
{
struct btrfs_super_block *sb = fs_info->super_copy;
struct btrfs_root *root = fs_info->tree_root;
- u32 leafsize = btrfs_super_leafsize(sb);
+ u32 nodesize = btrfs_super_nodesize(sb);
int ret;
ret = find_and_setup_root(root, fs_info, objectid, info_root);
@@ -947,7 +946,7 @@ static int setup_root_or_create_block(struct btrfs_fs_info *fs_info,
* million of places that assume a root has a valid ->node
*/
info_root->node =
- btrfs_find_create_tree_block(fs_info, 0, leafsize);
+ btrfs_find_create_tree_block(fs_info, 0, nodesize);
if (!info_root->node)
return -ENOMEM;
clear_extent_buffer_uptodate(NULL, info_root->node);
@@ -978,7 +977,7 @@ int btrfs_setup_all_roots(struct btrfs_fs_info *fs_info, u64 root_tree_bytenr,
root = fs_info->tree_root;
__setup_root(nodesize, leafsize, sectorsize, stripesize,
root, fs_info, BTRFS_ROOT_TREE_OBJECTID);
- blocksize = btrfs_level_size(root, btrfs_super_root_level(sb));
+ blocksize = root->nodesize;
generation = btrfs_super_generation(sb);
if (!root_tree_bytenr && !(flags & OPEN_CTREE_BACKUP_ROOT)) {
@@ -1146,7 +1145,8 @@ int btrfs_scan_fs_devices(int fd, const char *path,
return 0;
}
-int btrfs_setup_chunk_tree_and_device_map(struct btrfs_fs_info *fs_info)
+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;
@@ -1169,12 +1169,23 @@ int btrfs_setup_chunk_tree_and_device_map(struct btrfs_fs_info *fs_info)
if (ret)
return ret;
- blocksize = btrfs_level_size(fs_info->chunk_root,
- btrfs_super_chunk_root_level(sb));
+ 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))) {
+ warning("chunk_root_bytenr %llu is unaligned to %u, ignore it",
+ chunk_root_bytenr, btrfs_super_sectorsize(sb));
+ chunk_root_bytenr = 0;
+ }
+
+ if (!chunk_root_bytenr)
+ chunk_root_bytenr = btrfs_super_chunk_root(sb);
+ else
+ generation = 0;
+
fs_info->chunk_root->node = read_tree_block(fs_info->chunk_root,
- btrfs_super_chunk_root(sb),
+ chunk_root_bytenr,
blocksize, generation);
if (!extent_buffer_uptodate(fs_info->chunk_root->node)) {
if (fs_info->ignore_chunk_tree_error) {
@@ -1200,6 +1211,7 @@ int btrfs_setup_chunk_tree_and_device_map(struct btrfs_fs_info *fs_info)
static struct btrfs_fs_info *__open_ctree_fd(int fp, const char *path,
u64 sb_bytenr,
u64 root_tree_bytenr,
+ u64 chunk_root_bytenr,
enum btrfs_open_ctree_flags flags)
{
struct btrfs_fs_info *fs_info;
@@ -1273,7 +1285,7 @@ static struct btrfs_fs_info *__open_ctree_fd(int fp, const char *path,
if (ret)
goto out_devices;
- ret = btrfs_setup_chunk_tree_and_device_map(fs_info);
+ ret = btrfs_setup_chunk_tree_and_device_map(fs_info, chunk_root_bytenr);
if (ret)
goto out_chunk;
@@ -1305,22 +1317,35 @@ out:
struct btrfs_fs_info *open_ctree_fs_info(const char *filename,
u64 sb_bytenr, u64 root_tree_bytenr,
+ u64 chunk_root_bytenr,
enum btrfs_open_ctree_flags flags)
{
int fp;
+ int ret;
struct btrfs_fs_info *info;
int oflags = O_CREAT | O_RDWR;
+ struct stat st;
+
+ ret = stat(filename, &st);
+ if (ret < 0) {
+ error("cannot stat '%s': %s", filename, strerror(errno));
+ return NULL;
+ }
+ if (!(((st.st_mode & S_IFMT) == S_IFREG) || ((st.st_mode & S_IFMT) == S_IFBLK))) {
+ error("not a regular file or block device: %s", filename);
+ return NULL;
+ }
if (!(flags & OPEN_CTREE_WRITES))
oflags = O_RDONLY;
fp = open(filename, oflags, 0600);
if (fp < 0) {
- fprintf (stderr, "Could not open %s\n", filename);
+ error("cannot open '%s': %s", filename, strerror(errno));
return NULL;
}
info = __open_ctree_fd(fp, filename, sb_bytenr, root_tree_bytenr,
- flags);
+ chunk_root_bytenr, flags);
close(fp);
return info;
}
@@ -1332,7 +1357,7 @@ struct btrfs_root *open_ctree(const char *filename, u64 sb_bytenr,
/* This flags may not return fs_info with any valid root */
BUG_ON(flags & OPEN_CTREE_IGNORE_CHUNK_TREE_ERROR);
- info = open_ctree_fs_info(filename, sb_bytenr, 0, flags);
+ info = open_ctree_fs_info(filename, sb_bytenr, 0, 0, flags);
if (!info)
return NULL;
if (flags & __OPEN_CTREE_RETURN_CHUNK_ROOT)
@@ -1347,7 +1372,7 @@ struct btrfs_root *open_ctree_fd(int fp, const char *path, u64 sb_bytenr,
/* This flags may not return fs_info with any valid root */
BUG_ON(flags & OPEN_CTREE_IGNORE_CHUNK_TREE_ERROR);
- info = __open_ctree_fd(fp, path, sb_bytenr, 0, flags);
+ info = __open_ctree_fd(fp, path, sb_bytenr, 0, 0, flags);
if (!info)
return NULL;
if (flags & __OPEN_CTREE_RETURN_CHUNK_ROOT)
diff --git a/disk-io.h b/disk-io.h
index 30ccb2bd..b97b90be 100644
--- a/disk-io.h
+++ b/disk-io.h
@@ -109,7 +109,8 @@ void btrfs_cleanup_all_caches(struct btrfs_fs_info *fs_info);
int btrfs_scan_fs_devices(int fd, const char *path,
struct btrfs_fs_devices **fs_devices, u64 sb_bytenr,
int super_recover, int skip_devices);
-int btrfs_setup_chunk_tree_and_device_map(struct btrfs_fs_info *fs_info);
+int btrfs_setup_chunk_tree_and_device_map(struct btrfs_fs_info *fs_info,
+ u64 chunk_root_bytenr);
struct btrfs_root *open_ctree(const char *filename, u64 sb_bytenr,
enum btrfs_open_ctree_flags flags);
@@ -117,6 +118,7 @@ struct btrfs_root *open_ctree_fd(int fp, const char *path, u64 sb_bytenr,
enum btrfs_open_ctree_flags flags);
struct btrfs_fs_info *open_ctree_fs_info(const char *filename,
u64 sb_bytenr, u64 root_tree_bytenr,
+ u64 chunk_root_bytenr,
enum btrfs_open_ctree_flags flags);
int close_ctree_fs_info(struct btrfs_fs_info *fs_info);
static inline int close_ctree(struct btrfs_root *root)
diff --git a/extent-tree.c b/extent-tree.c
index b9b00f06..4a41717e 100644
--- a/extent-tree.c
+++ b/extent-tree.c
@@ -153,7 +153,7 @@ static int cache_block_group(struct btrfs_root *root,
GFP_NOFS);
}
if (key.type == BTRFS_METADATA_ITEM_KEY)
- last = key.objectid + root->leafsize;
+ last = key.objectid + root->nodesize;
else
last = key.objectid + key.offset;
}
@@ -1458,7 +1458,7 @@ int btrfs_lookup_extent_info(struct btrfs_trans_handle *trans,
if (metadata &&
!btrfs_fs_incompat(root->fs_info,
BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA)) {
- offset = root->leafsize;
+ offset = root->nodesize;
metadata = 0;
}
@@ -1493,14 +1493,14 @@ again:
path->slots[0]);
if (key.objectid == bytenr &&
key.type == BTRFS_EXTENT_ITEM_KEY &&
- key.offset == root->leafsize)
+ key.offset == root->nodesize)
ret = 0;
}
if (ret) {
btrfs_release_path(path);
key.type = BTRFS_EXTENT_ITEM_KEY;
- key.offset = root->leafsize;
+ key.offset = root->nodesize;
metadata = 0;
goto again;
}
@@ -1565,7 +1565,7 @@ int btrfs_set_block_flags(struct btrfs_trans_handle *trans,
key.offset = level;
key.type = BTRFS_METADATA_ITEM_KEY;
} else {
- key.offset = root->leafsize;
+ key.offset = root->nodesize;
key.type = BTRFS_EXTENT_ITEM_KEY;
}
@@ -1582,13 +1582,13 @@ again:
btrfs_item_key_to_cpu(path->nodes[0], &key,
path->slots[0]);
if (key.objectid == bytenr &&
- key.offset == root->leafsize &&
+ key.offset == root->nodesize &&
key.type == BTRFS_EXTENT_ITEM_KEY)
ret = 0;
}
if (ret) {
btrfs_release_path(path);
- key.offset = root->leafsize;
+ key.offset = root->nodesize;
key.type = BTRFS_EXTENT_ITEM_KEY;
goto again;
}
@@ -1686,7 +1686,7 @@ static int __btrfs_mod_ref(struct btrfs_trans_handle *trans,
}
} else {
bytenr = btrfs_node_blockptr(buf, i);
- num_bytes = btrfs_level_size(root, level - 1);
+ num_bytes = root->nodesize;
ret = process_func(trans, root, bytenr, num_bytes,
parent, ref_root, level - 1, 0);
if (ret) {
@@ -2750,7 +2750,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->leafsize,
+ ret = update_block_group(trans, root, ins->objectid, root->nodesize,
1, 0);
return ret;
}
@@ -3897,9 +3897,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->leafsize;
+ bytes_used += root->nodesize;
ret = btrfs_update_block_group(trans, root,
- key.objectid, root->leafsize, 1, 0);
+ key.objectid, root->nodesize, 1, 0);
BUG_ON(ret);
}
path.slots[0]++;
diff --git a/extent_io.c b/extent_io.c
index 88e92736..c99d3627 100644
--- a/extent_io.c
+++ b/extent_io.c
@@ -27,6 +27,7 @@
#include "list.h"
#include "ctree.h"
#include "volumes.h"
+#include "internal.h"
void extent_io_tree_init(struct extent_io_tree *tree)
{
@@ -770,7 +771,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->leafsize);
+ this_len = min(this_len, (u64)info->tree_root->nodesize);
eb = malloc(sizeof(struct extent_buffer) + this_len);
BUG_ON(!eb);
diff --git a/file-item.c b/file-item.c
index b46d7f10..7a3bbf35 100644
--- a/file-item.c
+++ b/file-item.c
@@ -25,6 +25,7 @@
#include "transaction.h"
#include "print-tree.h"
#include "crc32c.h"
+#include "internal.h"
#define MAX_CSUM_ITEMS(r,size) ((((BTRFS_LEAF_DATA_SIZE(r) - \
sizeof(struct btrfs_item) * 2) / \
diff --git a/find-root.c b/find-root.c
index 823db6ad..89d36119 100644
--- a/find-root.c
+++ b/find-root.c
@@ -30,7 +30,7 @@
/* Return value is the same as btrfs_find_root_search(). */
static int add_eb_to_result(struct extent_buffer *eb,
struct cache_tree *result,
- u32 leafsize,
+ u32 nodesize,
struct btrfs_find_root_filter *filter,
struct cache_extent **match)
{
@@ -80,7 +80,7 @@ static int add_eb_to_result(struct extent_buffer *eb,
/* Same level, insert it into the eb_tree */
if (level == gen_cache->highest_level) {
ret = add_cache_extent(&gen_cache->eb_tree,
- start, leafsize);
+ start, nodesize);
if (ret < 0 && ret != -EEXIST)
return ret;
ret = 0;
@@ -110,7 +110,7 @@ int btrfs_find_root_search(struct btrfs_fs_info *fs_info,
u64 chunk_offset = 0;
u64 chunk_size = 0;
u64 offset = 0;
- u32 leafsize = btrfs_super_leafsize(fs_info->super_copy);
+ u32 nodesize = btrfs_super_nodesize(fs_info->super_copy);
int suppress_errors = 0;
int ret = 0;
@@ -132,12 +132,12 @@ int btrfs_find_root_search(struct btrfs_fs_info *fs_info,
}
for (offset = chunk_offset;
offset < chunk_offset + chunk_size;
- offset += leafsize) {
- eb = read_tree_block_fs_info(fs_info, offset, leafsize,
+ offset += nodesize) {
+ eb = read_tree_block_fs_info(fs_info, offset, nodesize,
0);
if (!eb || IS_ERR(eb))
continue;
- ret = add_eb_to_result(eb, result, leafsize, filter,
+ ret = add_eb_to_result(eb, result, nodesize, filter,
match);
free_extent_buffer(eb);
if (ret)
diff --git a/free-space-cache.c b/free-space-cache.c
index d10a5f51..357d69e7 100644
--- a/free-space-cache.c
+++ b/free-space-cache.c
@@ -24,6 +24,7 @@
#include "extent_io.h"
#include "crc32c.h"
#include "bitops.h"
+#include "internal.h"
/*
* Kernel always uses PAGE_CACHE_SIZE for sectorsize, but we don't have
diff --git a/inode-map.c b/inode-map.c
index 346952b7..9e4dcd3c 100644
--- a/inode-map.c
+++ b/inode-map.c
@@ -19,6 +19,7 @@
#include "ctree.h"
#include "disk-io.h"
#include "transaction.h"
+#include "internal.h"
/*
* walks the btree of allocated inodes and find a hole.
diff --git a/internal.h b/internal.h
new file mode 100644
index 00000000..d5ea9986
--- /dev/null
+++ b/internal.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2007 Oracle. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#ifndef __INTERNAL_H__
+#define __INTERNAL_H__
+
+/*
+ * max/min macro
+ */
+#define min(x,y) ({ \
+ typeof(x) _x = (x); \
+ typeof(y) _y = (y); \
+ (void) (&_x == &_y); \
+ _x < _y ? _x : _y; })
+
+#define max(x,y) ({ \
+ typeof(x) _x = (x); \
+ typeof(y) _y = (y); \
+ (void) (&_x == &_y); \
+ _x > _y ? _x : _y; })
+
+#define min_t(type,x,y) \
+ ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; })
+#define max_t(type,x,y) \
+ ({ type __x = (x); type __y = (y); __x > __y ? __x: __y; })
+
+#endif
diff --git a/ioctl.h b/ioctl.h
index 771da231..7c808077 100644
--- a/ioctl.h
+++ b/ioctl.h
@@ -45,6 +45,14 @@ struct btrfs_ioctl_vol_args {
#define BTRFS_SUBVOL_CREATE_ASYNC (1ULL << 0)
#define BTRFS_SUBVOL_RDONLY (1ULL << 1)
#define BTRFS_SUBVOL_QGROUP_INHERIT (1ULL << 2)
+#define BTRFS_DEVICE_SPEC_BY_ID (1ULL << 3)
+
+#define BTRFS_VOL_ARG_V2_FLAGS_SUPPORTED \
+ (BTRFS_SUBVOL_CREATE_ASYNC | \
+ BTRFS_SUBVOL_RDONLY | \
+ BTRFS_SUBVOL_QGROUP_INHERIT | \
+ BTRFS_DEVICE_SPEC_BY_ID)
+
#define BTRFS_FSID_SIZE 16
#define BTRFS_UUID_SIZE 16
@@ -84,7 +92,10 @@ struct btrfs_ioctl_vol_args_v2 {
};
__u64 unused[4];
};
- char name[BTRFS_SUBVOL_NAME_MAX + 1];
+ union {
+ char name[BTRFS_SUBVOL_NAME_MAX + 1];
+ __u64 devid;
+ };
};
/*
@@ -709,6 +720,8 @@ static inline char *btrfs_err_str(enum btrfs_err_code err_code)
struct btrfs_ioctl_feature_flags[2])
#define BTRFS_IOC_GET_SUPPORTED_FEATURES _IOR(BTRFS_IOCTL_MAGIC, 57, \
struct btrfs_ioctl_feature_flags[3])
+#define BTRFS_IOC_RM_DEV_V2 _IOW(BTRFS_IOCTL_MAGIC, 58, \
+ struct btrfs_ioctl_vol_args_v2)
#ifdef __cplusplus
}
#endif
diff --git a/kerncompat.h b/kerncompat.h
index 0f207b7f..ee65aa72 100644
--- a/kerncompat.h
+++ b/kerncompat.h
@@ -241,26 +241,6 @@ static inline long IS_ERR(const void *ptr)
}
/*
- * max/min macro
- */
-#define min(x,y) ({ \
- typeof(x) _x = (x); \
- typeof(y) _y = (y); \
- (void) (&_x == &_y); \
- _x < _y ? _x : _y; })
-
-#define max(x,y) ({ \
- typeof(x) _x = (x); \
- typeof(y) _y = (y); \
- (void) (&_x == &_y); \
- _x > _y ? _x : _y; })
-
-#define min_t(type,x,y) \
- ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; })
-#define max_t(type,x,y) \
- ({ type __x = (x); type __y = (y); __x > __y ? __x: __y; })
-
-/*
* This looks more complex than it should be. But we need to
* get the type for the ~ right in round_down (it needs to be
* as wide as the result!), and we want to evaluate the macro
diff --git a/mkfs.c b/mkfs.c
index ea584042..5e79e0bc 100644
--- a/mkfs.c
+++ b/mkfs.c
@@ -340,7 +340,7 @@ static void print_version(void)
static u64 parse_profile(char *s)
{
- if (strcmp(s, "raid0") == 0) {
+ if (strcasecmp(s, "raid0") == 0) {
return BTRFS_BLOCK_GROUP_RAID0;
} else if (strcasecmp(s, "raid1") == 0) {
return BTRFS_BLOCK_GROUP_RAID1;
@@ -1333,7 +1333,7 @@ out:
return ret;
}
-int main(int ac, char **av)
+int main(int argc, char **argv)
{
char *file;
struct btrfs_root *root;
@@ -1395,7 +1395,7 @@ int main(int ac, char **av)
{ NULL, 0, NULL, 0}
};
- c = getopt_long(ac, av, "A:b:fl:n:s:m:d:L:O:r:U:VMKq",
+ c = getopt_long(argc, argv, "A:b:fl:n:s:m:d:L:O:r:U:VMKq",
long_options, NULL);
if (c < 0)
break;
@@ -1483,7 +1483,7 @@ int main(int ac, char **av)
sectorsize = max(sectorsize, (u32)sysconf(_SC_PAGESIZE));
saved_optind = optind;
- dev_cnt = ac - optind;
+ dev_cnt = argc - optind;
if (dev_cnt == 0)
print_usage(1);
@@ -1507,16 +1507,16 @@ int main(int ac, char **av)
}
while (dev_cnt-- > 0) {
- file = av[optind++];
+ file = argv[optind++];
if (is_block_device(file) == 1)
if (test_dev_for_mkfs(file, force_overwrite))
exit(1);
}
optind = saved_optind;
- dev_cnt = ac - optind;
+ dev_cnt = argc - optind;
- file = av[optind++];
+ file = argv[optind++];
ssd = is_ssd(file);
/*
@@ -1582,7 +1582,7 @@ int main(int ac, char **av)
for (i = saved_optind; i < saved_optind + dev_cnt; i++) {
char *path;
- path = av[i];
+ path = argv[i];
ret = test_minimum_size(path, nodesize);
if (ret < 0) {
fprintf(stderr, "Failed to check size for '%s': %s\n",
@@ -1728,7 +1728,7 @@ int main(int ac, char **av)
goto raid_groups;
while (dev_cnt-- > 0) {
- file = av[optind++];
+ file = argv[optind++];
/*
* open without O_EXCL so that the problem should not
diff --git a/print-tree.c b/print-tree.c
index 6704ff68..746f25be 100644
--- a/print-tree.c
+++ b/print-tree.c
@@ -285,19 +285,40 @@ static void print_uuids(struct extent_buffer *eb)
printf("fs uuid %s\nchunk uuid %s\n", fs_uuid, chunk_uuid);
}
+static void compress_type_to_str(u8 compress_type, char *ret)
+{
+ switch (compress_type) {
+ case BTRFS_COMPRESS_NONE:
+ strcpy(ret, "none");
+ break;
+ case BTRFS_COMPRESS_ZLIB:
+ strcpy(ret, "zlib");
+ break;
+ case BTRFS_COMPRESS_LZO:
+ strcpy(ret, "lzo");
+ break;
+ default:
+ sprintf(ret, "UNKNOWN.%d", compress_type);
+ }
+}
+
static void print_file_extent_item(struct extent_buffer *eb,
struct btrfs_item *item,
int slot,
struct btrfs_file_extent_item *fi)
{
int extent_type = btrfs_file_extent_type(eb, fi);
+ char compress_str[16];
+
+ compress_type_to_str(btrfs_file_extent_compression(eb, fi),
+ compress_str);
if (extent_type == BTRFS_FILE_EXTENT_INLINE) {
printf("\t\tinline extent data size %u "
- "ram %u compress %d\n",
+ "ram %u compress(%s)\n",
btrfs_file_extent_inline_item_len(eb, item),
btrfs_file_extent_inline_len(eb, slot, fi),
- btrfs_file_extent_compression(eb, fi));
+ compress_str);
return;
}
if (extent_type == BTRFS_FILE_EXTENT_PREALLOC) {
@@ -316,8 +337,7 @@ static void print_file_extent_item(struct extent_buffer *eb,
(unsigned long long)btrfs_file_extent_offset(eb, fi),
(unsigned long long)btrfs_file_extent_num_bytes(eb, fi),
(unsigned long long)btrfs_file_extent_ram_bytes(eb, fi));
- printf("\t\textent compression %d\n",
- btrfs_file_extent_compression(eb, fi));
+ printf("\t\textent compression(%s)\n", compress_str);
}
/* Caller should ensure sizeof(*ret) >= 16("DATA|TREE_BLOCK") */
@@ -1098,7 +1118,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 = btrfs_level_size(root, btrfs_header_level(eb) - 1);
+ size = root->nodesize;
for (i = 0; i < nr; i++) {
u64 blocknr = btrfs_node_blockptr(eb, i);
btrfs_node_key(eb, &disk_key, i);
diff --git a/qgroup-verify.c b/qgroup-verify.c
index 7e3afda8..1a0d38c3 100644
--- a/qgroup-verify.c
+++ b/qgroup-verify.c
@@ -59,6 +59,8 @@ struct qgroup_count {
static struct counts_tree {
struct rb_root root;
unsigned int num_groups;
+ unsigned int rescan_running:1;
+ unsigned int qgroup_inconsist:1;
} counts = { .root = RB_ROOT };
static struct rb_root by_bytenr = RB_ROOT;
@@ -544,8 +546,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 = btrfs_level_size(root,
- btrfs_header_level(eb) - 1);
+ new_num_bytes = root->nodesize;
ret = travel_tree(info, root, new_bytenr, new_num_bytes,
ref_parent);
@@ -701,6 +702,19 @@ static void add_bytes(u64 root_objectid, u64 num_bytes, int exclusive)
}
}
+static void read_qgroup_status(struct btrfs_path *path,
+ struct counts_tree *counts)
+{
+ struct btrfs_qgroup_status_item *status_item;
+ u64 flags;
+
+ status_item = btrfs_item_ptr(path->nodes[0], path->slots[0],
+ struct btrfs_qgroup_status_item);
+ flags = btrfs_qgroup_status_flags(path->nodes[0], status_item);
+ counts->qgroup_inconsist = flags & BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT;
+ counts->rescan_running = flags & BTRFS_QGROUP_STATUS_FLAG_RESCAN;
+}
+
static int load_quota_info(struct btrfs_fs_info *info)
{
int ret;
@@ -735,13 +749,17 @@ static int load_quota_info(struct btrfs_fs_info *info)
btrfs_item_key(leaf, &disk_key, i);
btrfs_disk_key_to_cpu(&key, &disk_key);
+ if (key.type == BTRFS_QGROUP_STATUS_KEY) {
+ read_qgroup_status(&path, &counts);
+ continue;
+ }
if (key.type == BTRFS_QGROUP_RELATION_KEY)
printf("Ignoring qgroup relation key %llu\n",
key.objectid);
/*
- * Ignore: BTRFS_QGROUP_STATUS_KEY,
- * BTRFS_QGROUP_LIMIT_KEY, BTRFS_QGROUP_RELATION_KEY
+ * Ignore: BTRFS_QGROUP_LIMIT_KEY,
+ * BTRFS_QGROUP_RELATION_KEY
*/
if (key.type != BTRFS_QGROUP_INFO_KEY)
continue;
@@ -762,7 +780,7 @@ static int load_quota_info(struct btrfs_fs_info *info)
tmproot = btrfs_read_fs_root_no_cache(info, &root_key);
if (tmproot && !IS_ERR(tmproot)) {
count->subvol_exists = 1;
- free(tmproot);
+ btrfs_free_fs_root(tmproot);
}
}
@@ -944,7 +962,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->leafsize;
+ num_bytes = info->extent_root->nodesize;
meta = 1;
}
@@ -1017,7 +1035,7 @@ static void print_fields_signed(long long bytes,
prefix, type, bytes, type, bytes_compressed);
}
-static void print_qgroup_difference(struct qgroup_count *count, int verbose)
+static int report_qgroup_difference(struct qgroup_count *count, int verbose)
{
int is_different;
struct qgroup_info *info = &count->info;
@@ -1047,19 +1065,34 @@ static void print_qgroup_difference(struct qgroup_count *count, int verbose)
print_fields_signed(excl_diff, excl_diff,
"diff:", "exclusive");
}
+ return (is_different && count->subvol_exists);
}
-void print_qgroup_report(int all)
+int report_qgroups(int all)
{
struct rb_node *node;
struct qgroup_count *c;
+ int ret = 0;
+ if (counts.rescan_running) {
+ if (all) {
+ printf(
+ "Qgroup rescan is running, qgroup counts difference is expected\n");
+ } else {
+ printf(
+ "Qgroup rescan is running, ignore qgroup check\n");
+ return ret;
+ }
+ }
+ if (counts.qgroup_inconsist && !counts.rescan_running)
+ fprintf(stderr, "Qgroup is already inconsistent before checking\n");
node = rb_first(&counts.root);
while (node) {
c = rb_entry(node, struct qgroup_count, rb_node);
- print_qgroup_difference(c, all);
+ ret |= report_qgroup_difference(c, all);
node = rb_next(node);
}
+ return ret;
}
int qgroup_verify_all(struct btrfs_fs_info *info)
diff --git a/qgroup-verify.h b/qgroup-verify.h
index 7d91c197..37474650 100644
--- a/qgroup-verify.h
+++ b/qgroup-verify.h
@@ -23,7 +23,7 @@
#include "ctree.h"
int qgroup_verify_all(struct btrfs_fs_info *info);
-void print_qgroup_report(int all);
+int report_qgroups(int all);
int print_extent_state(struct btrfs_fs_info *info, u64 subvol);
diff --git a/tests/cli-tests.sh b/tests/cli-tests.sh
new file mode 100755
index 00000000..e65e7f50
--- /dev/null
+++ b/tests/cli-tests.sh
@@ -0,0 +1,41 @@
+#!/bin/bash
+#
+# command line interface coverage tests
+
+unset TOP
+unset LANG
+LANG=C
+SCRIPT_DIR=$(dirname $(readlink -f $0))
+TOP=$(readlink -f $SCRIPT_DIR/../)
+TEST_DEV=${TEST_DEV:-}
+RESULTS="$TOP/tests/cli-tests-results.txt"
+IMAGE="$TOP/tests/test.img"
+
+source $TOP/tests/common
+
+export TOP
+export RESULTS
+export LANG
+export IMAGE
+
+rm -f $RESULTS
+
+check_prereq btrfs
+
+# The tests are driven by their custom script called 'test.sh'
+
+for i in $(find $TOP/tests/cli-tests -maxdepth 1 -mindepth 1 -type d \
+ ${TEST:+-name "$TEST"} | sort)
+do
+ name=$(basename $i)
+ cd $i
+ if [ -x test.sh ]; then
+ echo "=== Entering $i" >> $RESULTS
+ echo " [TEST/cli] $name"
+ ./test.sh
+ if [ $? -ne 0 ]; then
+ _fail "test failed for case $(basename $i)"
+ fi
+ fi
+ cd $TOP
+done
diff --git a/tests/cli-tests/001-btrfs/test.sh b/tests/cli-tests/001-btrfs/test.sh
new file mode 100755
index 00000000..1de2f6f2
--- /dev/null
+++ b/tests/cli-tests/001-btrfs/test.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+# test commands of btrfs
+
+source $TOP/tests/common
+
+check_prereq btrfs
+
+# returns 1
+run_mayfail $TOP/btrfs || true
+run_check $TOP/btrfs version
+run_check $TOP/btrfs version --
+run_check $TOP/btrfs help
+run_check $TOP/btrfs help --
+run_check $TOP/btrfs --help
+run_check $TOP/btrfs --help --full
diff --git a/tests/cli-tests/002-balance-full-no-filters/test.sh b/tests/cli-tests/002-balance-full-no-filters/test.sh
new file mode 100755
index 00000000..c2757f24
--- /dev/null
+++ b/tests/cli-tests/002-balance-full-no-filters/test.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# coverage of balance --full-balance
+
+source $TOP/tests/common
+
+check_prereq mkfs.btrfs
+check_prereq btrfs
+
+setup_root_helper
+prepare_test_dev 2g
+
+run_check $TOP/mkfs.btrfs -f $IMAGE
+run_check_mount_test_dev
+
+run_check $SUDO_HELPER $TOP/btrfs balance start --full-balance $TEST_MNT
+run_check $SUDO_HELPER $TOP/btrfs balance start $TEST_MNT
+run_check $SUDO_HELPER $TOP/btrfs balance --full-balance $TEST_MNT
+run_check $SUDO_HELPER $TOP/btrfs balance $TEST_MNT
+
+run_check_umount_test_dev
diff --git a/tests/common b/tests/common
index 61780486..91682eff 100644
--- a/tests/common
+++ b/tests/common
@@ -61,6 +61,32 @@ run_mayfail()
fi
}
+# first argument is error message to print if it fails, otherwise
+# same as run_check but expects the command to fail, output is logged
+run_mustfail()
+{
+ local msg
+
+ msg="$1"
+ shift
+
+ echo "############### $@" >> $RESULTS 2>&1
+ if [ "$TEST_LOG" = 'tty' ]; then echo "CMD(mustfail): $@" > /dev/tty; fi
+ if [ "$1" = 'root_helper' ]; then
+ "$@" >> $RESULTS 2>&1
+ else
+ $INSTRUMENT "$@" >> $RESULTS 2>&1
+ fi
+ if [ $? != 0 ]; then
+ echo "failed (expected): $@" >> $RESULTS
+ return 0
+ else
+ echo "succeeded (unexpected!): $@" >> $RESULTS
+ _fail "unexpected success: $msg"
+ return 1
+ fi
+}
+
check_prereq()
{
if ! [ -f $TOP/$1 ]; then
diff --git a/tests/convert-tests.sh b/tests/convert-tests.sh
index 0bfb41f8..06d8419e 100644..100755
--- a/tests/convert-tests.sh
+++ b/tests/convert-tests.sh
@@ -10,16 +10,95 @@ LANG=C
SCRIPT_DIR=$(dirname $(readlink -f $0))
TOP=$(readlink -f $SCRIPT_DIR/../)
RESULTS="$TOP/tests/convert-tests-results.txt"
+# how many files to create.
+DATASET_SIZE=50
source $TOP/tests/common
rm -f $RESULTS
setup_root_helper
-prepare_test_dev 256M
+prepare_test_dev 512M
CHECKSUMTMP=$(mktemp --tmpdir btrfs-progs-convert.XXXXXXXXXX)
+generate_dataset() {
+
+ dataset_type="$1"
+ dirpath=$TEST_MNT/$dataset_type
+ run_check $SUDO_HELPER mkdir -p $dirpath
+
+ case $dataset_type in
+ small)
+ for num in $(seq 1 $DATASET_SIZE); do
+ run_check $SUDO_HELPER dd if=/dev/urandom of=$dirpath/$dataset_type.$num bs=10K \
+ count=1 >/dev/null 2>&1
+ done
+ ;;
+
+ hardlink)
+ for num in $(seq 1 $DATASET_SIZE); do
+ run_check $SUDO_HELPER touch $dirpath/$dataset_type.$num
+ run_check $SUDO_HELPER ln $dirpath/$dataset_type.$num $dirpath/hlink.$num
+ done
+ ;;
+
+ symlink)
+ for num in $(seq 1 $DATASET_SIZE); do
+ run_check $SUDO_HELPER touch $dirpath/$dataset_type.$num
+ run_check $SUDO_HELPER ln -s $dirpath/$dataset_type.$num $dirpath/slink.$num
+ done
+ ;;
+
+ brokenlink)
+ for num in $(seq 1 $DATASET_SIZE); do
+ run_check $SUDO_HELPER ln -s $dirpath/$dataset_type.$num $dirpath/blink.$num
+ done
+ ;;
+
+ perm)
+ for modes in 777 775 755 750 700 666 664 644 640 600 444 440 400 000 \
+ 1777 1775 1755 1750 1700 1666 1664 1644 1640 1600 1444 1440 1400 1000 \
+ 2777 2775 2755 2750 2700 2666 2664 2644 2640 2600 2444 2440 2400 2000 \
+ 4777 4775 4755 4750 4700 4666 4664 4644 4640 4600 4444 4440 4400 4000; do
+ if [[ "$modes" == *9* ]] || [[ "$modes" == *8* ]]
+ then
+ continue;
+ else
+ run_check $SUDO_HELPER touch $dirpath/$dataset_type.$modes
+ run_check $SUDO_HELPER chmod $modes $dirpath/$dataset_type.$modes
+ fi
+ done
+ ;;
+
+ sparse)
+ for num in $(seq 1 $DATASET_SIZE); do
+ run_check $SUDO_HELPER dd if=/dev/urandom of=$dirpath/$dataset_type.$num bs=10K \
+ count=1 >/dev/null 2>&1
+ run_check $SUDO_HELPER truncate -s 500K $dirpath/$dataset_type.$num
+ run_check $SUDO_HELPER dd if=/dev/urandom of=$dirpath/$dataset_type.$num bs=10K \
+ oflag=append conv=notrunc count=1 >/dev/null 2>&1
+ run_check $SUDO_HELPER truncate -s 800K $dirpath/$dataset_type.$num
+ done
+ ;;
+
+ acls)
+ for num in $(seq 1 $DATASET_SIZE); do
+ run_check $SUDO_HELPER touch $dirpath/$dataset_type.$num
+ run_check $SUDO_HELPER setfacl -m "u:root:x" $dirpath/$dataset_type.$num
+ run_check $SUDO_HELPER setfattr -n user.foo -v bar$num $dirpath/$dataset_type.$num
+ done
+ ;;
+ esac
+}
+
+populate_fs() {
+
+ for dataset_type in 'small' 'hardlink' 'symlink' 'brokenlink' 'perm' 'sparse' 'acls'; do
+ generate_dataset "$dataset_type"
+ done
+}
+
convert_test() {
local features
local nodesize
@@ -39,15 +118,16 @@ convert_test() {
# when test image is on NFS and would not be writable for root
run_check truncate -s 0 $TEST_DEV
# 256MB is the smallest acceptable btrfs image.
- run_check truncate -s 256M $TEST_DEV
+ run_check truncate -s 512M $TEST_DEV
run_check $* -F $TEST_DEV
# create a file to check btrfs-convert can convert regular file
# correct
run_check_mount_test_dev
+ populate_fs
run_check $SUDO_HELPER dd if=/dev/zero of=$TEST_MNT/test bs=$nodesize \
- count=1 1>/dev/null 2>&1
- run_check_stdout md5sum $TEST_MNT/test > $CHECKSUMTMP
+ count=1 >/dev/null 2>&1
+ run_check_stdout find $TEST_MNT -type f ! -name 'image' -exec md5sum {} \+ > $CHECKSUMTMP
run_check_umount_test_dev
run_check $TOP/btrfs-convert ${features:+-O "$features"} -N "$nodesize" $TEST_DEV
@@ -56,7 +136,7 @@ convert_test() {
run_check_mount_test_dev
run_check_stdout md5sum -c $CHECKSUMTMP |
- grep -q 'OK' || _fail "file validation failed."
+ grep -q 'FAILED' && _fail "file validation failed."
run_check_umount_test_dev
}
diff --git a/tests/fuzz-tests/images/bko-96971-btrfs-image.raw.xz b/tests/fuzz-tests/images/bko-96971-btrfs-image.raw.xz
new file mode 100644
index 00000000..21aa33b0
--- /dev/null
+++ b/tests/fuzz-tests/images/bko-96971-btrfs-image.raw.xz
Binary files differ
diff --git a/tests/fuzz-tests/images/bko-96971-btrfs-image.txt b/tests/fuzz-tests/images/bko-96971-btrfs-image.txt
new file mode 100644
index 00000000..ff85540d
--- /dev/null
+++ b/tests/fuzz-tests/images/bko-96971-btrfs-image.txt
@@ -0,0 +1,69 @@
+URL: https://bugzilla.kernel.org/show_bug.cgi?id=96971
+ Lukas Lueg 2015-04-20 23:01:44 UTC
+
+I've identified some problems in the btrfs code and attached a btrfs-image
+which causes the userland tools to crash and the kernel to immediately freeze
+once the filesystem get's mounted and one of the files is accessed. Putting
+the image onto a usb-drive gives you a freeze-on-a-stick :-)
+
+"btrfs check" crashes due to a SIGFPE in count_csum_range(). The culprit is
+struct btrfs_root->fs_info->super_copy->csum_size being 0, which goes
+unchecked before entering a division. I was not able to identify where the
+kernel crashes (system goes down the tubes), yet the problem is probably the
+same.
+
+"btrfs version" is v3.19.1; bug is also present in latest git (kdave and
+unstable) as of 2015/04/21
+
+
+Full gdb output:
+
+gdb btrfs
+GNU gdb (GDB) Fedora 7.8.2-38.fc21
+Copyright (C) 2014 Free Software Foundation, Inc.
+License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
+This is free software: you are free to change and redistribute it.
+There is NO WARRANTY, to the extent permitted by law. Type "show copying"
+and "show warranty" for details.
+This GDB was configured as "x86_64-redhat-linux-gnu".
+Type "show configuration" for configuration details.
+For bug reporting instructions, please see:
+<http://www.gnu.org/software/gdb/bugs/>.
+Find the GDB manual and other documentation resources online at:
+<http://www.gnu.org/software/gdb/documentation/>.
+For help, type "help".
+Type "apropos word" to search for commands related to "word"...
+Reading symbols from btrfs...Reading symbols from /usr/lib/debug/usr/sbin/btrfs.debug...done.
+done.
+(gdb) run check btrfs_fukked.bin
+Starting program: /usr/sbin/btrfs check btrfs_fukked.bin
+[Thread debugging using libthread_db enabled]
+Using host libthread_db library "/lib64/libthread_db.so.1".
+Checking filesystem on btrfs_fukked.bin
+UUID: cdd8684f-9eb1-40a4-91ec-1ed7c3cb444c
+checking extents
+checking free space cache
+checking fs roots
+
+Program received signal SIGFPE, Arithmetic exception.
+count_csum_range (root=<optimized out>, root=<optimized out>,
+ found=<synthetic pointer>, len=7385088, start=7471104) at cmds-check.c:1455
+1455 csum_end = key.offset + (size / csum_size) * root->sectorsize;
+(gdb) bt
+#0 count_csum_range (root=<optimized out>, root=<optimized out>,
+ found=<synthetic pointer>, len=7385088, start=7471104) at cmds-check.c:1455
+#1 process_file_extent (active_node=0x7fffffffd710, key=0x7fffffffd680,
+ slot=11, eb=<optimized out>, root=0x894b10) at cmds-check.c:1551
+#2 process_one_leaf (wc=0x7fffffffd6c0, eb=<optimized out>, root=0x894b10)
+ at cmds-check.c:1617
+#3 walk_down_tree (level=<synthetic pointer>, wc=0x7fffffffd6c0,
+ path=0x7fffffffd7f0, root=0x894b10) at cmds-check.c:1742
+#4 check_fs_root (wc=0x7fffffffd6c0, root_cache=0x7fffffffdb20, root=0x894b10)
+ at cmds-check.c:3380
+#5 check_fs_roots (root_cache=root_cache@entry=0x7fffffffdb20, root=0x894b10)
+ at cmds-check.c:3516
+#6 0x0000000000428aea in cmd_check (argc=<optimized out>,
+ argv=<optimized out>) at cmds-check.c:9465
+#7 0x000000000040e5a2 in main (argc=2, argv=0x7fffffffdeb0) at btrfs.c:245
+(gdb) p csum_size
+$2 = 0
diff --git a/tests/misc-tests/013-subvolume-sync-crash/test.sh b/tests/misc-tests/013-subvolume-sync-crash/test.sh
new file mode 100755
index 00000000..c7955269
--- /dev/null
+++ b/tests/misc-tests/013-subvolume-sync-crash/test.sh
@@ -0,0 +1,49 @@
+#!/bin/bash
+#
+# Verify that subovolume sync waits until the subvolume is cleaned and does not
+# crash at the end
+
+source $TOP/tests/common
+
+check_prereq mkfs.btrfs
+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
+run_check $SUDO_HELPER chmod a+rw $TEST_MNT
+
+cd $TEST_MNT
+
+for i in `seq 5`; do
+ run_check dd if=/dev/zero of=file$i bs=1M count=10
+done
+
+# 128 is minimum
+for sn in `seq 130`;do
+ run_check $SUDO_HELPER $TOP/btrfs subvolume snapshot . snap$sn
+ for i in `seq 10`; do
+ run_check dd if=/dev/zero of=snap$sn/file$i bs=1M count=1
+ done
+done
+
+run_check $SUDO_HELPER $TOP/btrfs subvolume list .
+run_check $SUDO_HELPER $TOP/btrfs subvolume list -d .
+
+idtodel=`run_check_stdout $SUDO_HELPER $TOP/btrfs inspect-internal rootid snap3`
+
+# delete, sync after some time
+run_check $SUDO_HELPER $TOP/btrfs subvolume delete -c snap*
+{ sleep 5; run_check $TOP/btrfs filesystem sync $TEST_MNT; } &
+
+run_check $SUDO_HELPER $TOP/btrfs subvolume sync .
+
+run_check $TOP/btrfs filesystem sync $TEST_MNT
+run_check $SUDO_HELPER $TOP/btrfs subvolume list -d .
+
+wait
+cd ..
+
+run_check $SUDO_HELPER umount $TEST_MNT
diff --git a/tests/misc-tests/014-filesystem-label/test.sh b/tests/misc-tests/014-filesystem-label/test.sh
new file mode 100755
index 00000000..a5e08ccc
--- /dev/null
+++ b/tests/misc-tests/014-filesystem-label/test.sh
@@ -0,0 +1,69 @@
+#!/bin/bash
+#
+# test label settings
+
+source $TOP/tests/common
+
+check_prereq mkfs.btrfs
+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
+run_check $SUDO_HELPER chmod a+rw $TEST_MNT
+
+cd $TEST_MNT
+run_check $SUDO_HELPER $TOP/btrfs filesystem label $TEST_MNT
+# shortest label
+run_check $SUDO_HELPER $TOP/btrfs filesystem label $TEST_MNT a
+run_check $SUDO_HELPER $TOP/btrfs filesystem label $TEST_MNT
+run_check $SUDO_HELPER $TOP/btrfs filesystem label $TEST_MNT ''
+
+longlabel=\
+0123456789\
+0123456789\
+0123456789\
+0123456789\
+0123456789\
+\
+0123456789\
+0123456789\
+0123456789\
+0123456789\
+0123456789\
+\
+0123456789\
+0123456789\
+0123456789\
+0123456789\
+0123456789\
+\
+0123456789\
+0123456789\
+0123456789\
+0123456789\
+0123456789\
+\
+0123456789\
+0123456789\
+0123456789\
+0123456789\
+0123456789\
+\
+01234
+
+run_check $SUDO_HELPER $TOP/btrfs filesystem label $TEST_MNT "$longlabel"
+run_check $SUDO_HELPER $TOP/btrfs filesystem label $TEST_MNT
+# 256, must fail
+run_mustfail "label 256 bytes long succeeded" \
+ $SUDO_HELPER $TOP/btrfs filesystem label $TEST_MNT "$longlabel"5
+run_check $SUDO_HELPER $TOP/btrfs filesystem label $TEST_MNT
+run_mustfail "label 2 * 255 bytes long succeeded" \
+ $SUDO_HELPER $TOP/btrfs filesystem label $TEST_MNT "$longlabel$longlabel"
+run_check $SUDO_HELPER $TOP/btrfs filesystem label $TEST_MNT
+
+cd ..
+
+run_check $SUDO_HELPER umount $TEST_MNT
diff --git a/tests/mkfs-tests/001-basic-profiles/test.sh b/tests/mkfs-tests/001-basic-profiles/test.sh
index 2747d429..a6769214 100755
--- a/tests/mkfs-tests/001-basic-profiles/test.sh
+++ b/tests/mkfs-tests/001-basic-profiles/test.sh
@@ -87,5 +87,7 @@ test_mkfs_multi -d raid5 -m raid5
test_mkfs_multi -d raid5 -m raid5 --mixed
test_mkfs_multi -d raid6 -m raid6
test_mkfs_multi -d raid6 -m raid6 --mixed
+test_mkfs_multi -d dup -m dup
+test_mkfs_multi -d dup -m dup --mixed
cleanup_devices
diff --git a/tests/mkfs-tests/005-long-device-name-for-ssd/test.sh b/tests/mkfs-tests/005-long-device-name-for-ssd/test.sh
index c89ee0e1..cff495e6 100755
--- a/tests/mkfs-tests/005-long-device-name-for-ssd/test.sh
+++ b/tests/mkfs-tests/005-long-device-name-for-ssd/test.sh
@@ -21,7 +21,8 @@ run_check truncate -s2g img
loopdev=`run_check_stdout $SUDO_HELPER losetup --find --show img`
run_check $SUDO_HELPER dmsetup create $dmname --table "0 1048576 linear $loopdev 0"
-base=`basename "$loopdev"`
+dmbase=`readlink -f $dmdev`
+base=`basename "$dmbase"`
rot=/sys/class/block/$base/queue/rotational
# switch rotational
diff --git a/utils.c b/utils.c
index eabc36dc..7e457026 100644
--- a/utils.c
+++ b/utils.c
@@ -253,7 +253,7 @@ int make_btrfs(int fd, struct btrfs_mkfs_config *cfg)
btrfs_set_super_cache_generation(&super, -1);
btrfs_set_super_incompat_flags(&super, cfg->features);
if (cfg->label)
- strncpy(super.label, cfg->label, BTRFS_LABEL_SIZE - 1);
+ __strncpy_null(super.label, cfg->label, BTRFS_LABEL_SIZE - 1);
/* create the tree of root objects */
memset(buf->data, 0, cfg->nodesize);
@@ -842,8 +842,11 @@ static int btrfs_wipe_existing_sb(int fd)
memset(buf, 0, len);
ret = pwrite(fd, buf, len, offset);
- if (ret != len) {
- fprintf(stderr, "ERROR: cannot wipe existing superblock\n");
+ if (ret < 0) {
+ error("cannot wipe existing superblock: %s", strerror(errno));
+ ret = -1;
+ } else if (ret != len) {
+ error("cannot wipe existing superblock: wrote %d of %zd", ret, len);
ret = -1;
}
fsync(fd);
@@ -853,8 +856,8 @@ out:
return ret;
}
-int btrfs_prepare_device(int fd, char *file, int zero_end, u64 *block_count_ret,
- u64 max_block_count, int discard)
+int btrfs_prepare_device(int fd, const char *file, int zero_end,
+ u64 *block_count_ret, u64 max_block_count, int discard)
{
u64 block_count;
struct stat st;
@@ -862,13 +865,13 @@ int btrfs_prepare_device(int fd, char *file, int zero_end, u64 *block_count_ret,
ret = fstat(fd, &st);
if (ret < 0) {
- fprintf(stderr, "unable to stat %s\n", file);
+ error("unable to stat %s: %s", file, strerror(errno));
return 1;
}
block_count = btrfs_device_size(fd, &st);
if (block_count == 0) {
- fprintf(stderr, "unable to find %s size\n", file);
+ error("unable to determine size of %s", file);
return 1;
}
if (max_block_count)
@@ -896,15 +899,13 @@ int btrfs_prepare_device(int fd, char *file, int zero_end, u64 *block_count_ret,
ZERO_DEV_BYTES, block_count);
if (ret < 0) {
- fprintf(stderr, "ERROR: failed to zero device '%s' - %s\n",
- file, strerror(-ret));
+ error("failed to zero device '%s': %s", file, strerror(-ret));
return 1;
}
ret = btrfs_wipe_existing_sb(fd);
if (ret < 0) {
- fprintf(stderr, "ERROR: cannot wipe superblocks on '%s'\n",
- file);
+ error("cannot wipe superblocks on %s", file);
return 1;
}
@@ -1051,11 +1052,10 @@ int get_btrfs_mount(const char *dev, char *mp, size_t mp_size)
ret = is_block_device(dev);
if (ret <= 0) {
if (!ret) {
- fprintf(stderr, "%s is not a block device\n", dev);
+ error("not a block device: %s", dev);
ret = -EINVAL;
} else {
- fprintf(stderr, "Could not check %s: %s\n",
- dev, strerror(-ret));
+ error("cannot check %s: %s", dev, strerror(-ret));
}
goto out;
}
@@ -1063,7 +1063,7 @@ int get_btrfs_mount(const char *dev, char *mp, size_t mp_size)
fd = open(dev, O_RDONLY);
if (fd < 0) {
ret = -errno;
- fprintf(stderr, "Could not open %s: %s\n", dev, strerror(errno));
+ error("cannot open %s: %s", dev, strerror(errno));
goto out;
}
@@ -1122,43 +1122,31 @@ int btrfs_open_dir(const char *path, DIR **dirstream, int verbose)
int ret;
if (statfs(path, &stfs) != 0) {
- if (verbose)
- fprintf(stderr,
- "ERROR: can't access '%s': %s\n",
- path, strerror(errno));
+ error_on(verbose, "cannot access '%s': %s", path,
+ strerror(errno));
return -1;
}
if (stfs.f_type != BTRFS_SUPER_MAGIC) {
- if (verbose)
- fprintf(stderr,
- "ERROR: not a btrfs filesystem: %s\n",
- path);
+ error_on(verbose, "not a btrfs filesystem: %s", path);
return -2;
}
if (stat(path, &st) != 0) {
- if (verbose)
- fprintf(stderr,
- "ERROR: can't access '%s': %s\n",
- path, strerror(errno));
+ error_on(verbose, "cannot access '%s': %s", path,
+ strerror(errno));
return -1;
}
if (!S_ISDIR(st.st_mode)) {
- if (verbose)
- fprintf(stderr,
- "ERROR: not a directory: %s\n",
- path);
+ error_on(verbose, "not a directory: %s", path);
return -3;
}
ret = open_file_or_dir(path, dirstream);
if (ret < 0) {
- if (verbose)
- fprintf(stderr,
- "ERROR: can't access '%s': %s\n",
- path, strerror(errno));
+ error_on(verbose, "cannot access '%s': %s", path,
+ strerror(errno));
}
return ret;
@@ -1434,7 +1422,8 @@ int check_mounted(const char* file)
fd = open(file, O_RDONLY);
if (fd < 0) {
- fprintf (stderr, "check_mounted(): Could not open %s\n", file);
+ error("mount check: cannot open %s: %s", file,
+ strerror(errno));
return -errno;
}
@@ -1522,8 +1511,8 @@ int btrfs_register_one_device(const char *fname)
fd = open("/dev/btrfs-control", O_RDWR);
if (fd < 0) {
- fprintf(stderr, "failed to open /dev/btrfs-control "
- "skipping device registration: %s\n",
+ warning(
+ "failed to open /dev/btrfs-control, skipping device registration: %s",
strerror(errno));
return -errno;
}
@@ -1531,8 +1520,8 @@ int btrfs_register_one_device(const char *fname)
strncpy_null(args.name, fname);
ret = ioctl(fd, BTRFS_IOC_SCAN_DEV, &args);
if (ret < 0) {
- fprintf(stderr, "ERROR: device scan failed '%s' - %s\n",
- fname, strerror(errno));
+ error("device scan failed on '%s': %s", fname,
+ strerror(errno));
ret = -errno;
}
close(fd);
@@ -1545,7 +1534,8 @@ int btrfs_register_one_device(const char *fname)
*/
int btrfs_register_all_devices(void)
{
- int err;
+ int err = 0;
+ int ret = 0;
struct btrfs_fs_devices *fs_devices;
struct btrfs_device *device;
struct list_head *all_uuids;
@@ -1554,16 +1544,15 @@ int btrfs_register_all_devices(void)
list_for_each_entry(fs_devices, all_uuids, list) {
list_for_each_entry(device, &fs_devices->devices, dev_list) {
- if (*device->name) {
+ if (*device->name)
err = btrfs_register_one_device(device->name);
- if (err < 0)
- return err;
- if (err > 0)
- return -err;
- }
+
+ if (err)
+ ret++;
}
}
- return 0;
+
+ return ret;
}
int btrfs_device_already_in_root(struct btrfs_root *root, int fd,
@@ -1672,6 +1661,13 @@ int pretty_size_snprintf(u64 size, char *str, size_t str_size, unsigned unit_mod
size /= mult;
num_divs++;
}
+ /*
+ * If the value is smaller than base, we didn't do any
+ * division, in that case, base should be 1, not original
+ * base, or the unit will be wrong
+ */
+ if (num_divs == 0)
+ base = 1;
}
if (num_divs >= ARRAY_SIZE(unit_suffix_binary)) {
@@ -1687,7 +1683,7 @@ int pretty_size_snprintf(u64 size, char *str, size_t str_size, unsigned unit_mod
}
/*
- * __strncpy__null - strncpy with null termination
+ * __strncpy_null - strncpy with null termination
* @dest: the target array
* @src: the source string
* @n: maximum bytes to copy (size of *dest)
@@ -1698,7 +1694,7 @@ int pretty_size_snprintf(u64 size, char *str, size_t str_size, unsigned unit_mod
* byte ('\0'), to the buffer pointed to by dest, up to a maximum
* of n bytes. Then ensure that dest is null-terminated.
*/
-char *__strncpy__null(char *dest, const char *src, size_t n)
+char *__strncpy_null(char *dest, const char *src, size_t n)
{
strncpy(dest, src, n);
if (n > 0)
@@ -1750,8 +1746,8 @@ static int set_label_unmounted(const char *dev, const char *label)
return -1;
trans = btrfs_start_transaction(root, 1);
- snprintf(root->fs_info->super_copy->label, BTRFS_LABEL_SIZE, "%s",
- label);
+ __strncpy_null(root->fs_info->super_copy->label, label, BTRFS_LABEL_SIZE - 1);
+
btrfs_commit_transaction(trans, root);
/* Now we close it since we are done. */
@@ -1759,9 +1755,10 @@ static int set_label_unmounted(const char *dev, const char *label)
return 0;
}
-static int set_label_mounted(const char *mount_path, const char *label)
+static int set_label_mounted(const char *mount_path, const char *labelp)
{
int fd;
+ char label[BTRFS_LABEL_SIZE];
fd = open(mount_path, O_RDONLY | O_NOATIME);
if (fd < 0) {
@@ -1769,6 +1766,8 @@ static int set_label_mounted(const char *mount_path, const char *label)
return -1;
}
+ memset(label, 0, sizeof(label));
+ __strncpy_null(label, labelp, BTRFS_LABEL_SIZE - 1);
if (ioctl(fd, BTRFS_IOC_SET_FSLABEL, label) < 0) {
fprintf(stderr, "ERROR: unable to set label %s\n",
strerror(errno));
@@ -1798,7 +1797,8 @@ int get_label_unmounted(const char *dev, char *label)
if(!root)
return -1;
- memcpy(label, root->fs_info->super_copy->label, BTRFS_LABEL_SIZE);
+ __strncpy_null(label, root->fs_info->super_copy->label,
+ BTRFS_LABEL_SIZE - 1);
/* Now we close it since we are done. */
close_ctree(root);
@@ -1833,7 +1833,7 @@ int get_label_mounted(const char *mount_path, char *labelp)
return ret;
}
- strncpy(labelp, label, sizeof(label));
+ __strncpy_null(labelp, label, BTRFS_LABEL_SIZE - 1);
close(fd);
return 0;
}
@@ -1868,25 +1868,6 @@ int set_label(const char *btrfs_dev, const char *label)
}
/*
- * Unsafe subvolume check.
- *
- * This only checks ino == BTRFS_FIRST_FREE_OBJECTID, even it is not in a
- * btrfs mount point.
- * Must use together with other reliable method like btrfs ioctl.
- */
-static int __is_subvol(const char *path)
-{
- struct stat st;
- int ret;
-
- ret = lstat(path, &st);
- if (ret < 0)
- return ret;
-
- return st.st_ino == BTRFS_FIRST_FREE_OBJECTID;
-}
-
-/*
* A not-so-good version fls64. No fascinating optimization since
* no one except parse_size use it
*/
@@ -2006,7 +1987,7 @@ u64 parse_qgroupid(const char *p)
path:
/* Path format like subv at 'my_subvol' is the fallback case */
- ret = __is_subvol(p);
+ ret = test_issubvolume(p);
if (ret < 0 || !ret)
goto err;
fd = open(p, O_RDONLY);
@@ -2187,8 +2168,7 @@ int get_fs_info(char *path, struct btrfs_ioctl_fs_info_args *fi_args,
fd = open(path, O_RDONLY);
if (fd < 0) {
ret = -errno;
- fprintf(stderr, "Couldn't open %s: %s\n",
- path, strerror(errno));
+ error("cannot open %s: %s", path, strerror(errno));
goto out;
}
ret = check_mounted_where(fd, path, mp, sizeof(mp),
@@ -2371,9 +2351,7 @@ out:
* 0 for nothing found
* -1 for internal error
*/
-static int
-check_overwrite(
- char *device)
+static int check_overwrite(const char *device)
{
const char *type;
blkid_probe pr = NULL;
@@ -2477,16 +2455,13 @@ int test_num_disk_vs_raid(u64 metadata_profile, u64 data_profile,
case 2:
allowed |= BTRFS_BLOCK_GROUP_RAID0 | BTRFS_BLOCK_GROUP_RAID1 |
BTRFS_BLOCK_GROUP_RAID5;
- break;
case 1:
allowed |= BTRFS_BLOCK_GROUP_DUP;
}
if (dev_cnt > 1 &&
((metadata_profile | data_profile) & BTRFS_BLOCK_GROUP_DUP)) {
- fprintf(stderr,
- "ERROR: DUP is not allowed when FS has multiple devices\n");
- return 1;
+ warning("DUP is not recommended on filesystem with multiple devices");
}
if (metadata_profile & ~allowed) {
fprintf(stderr,
@@ -2535,7 +2510,7 @@ int group_profile_max_safe_loss(u64 flags)
* 1: something is wrong, an error is printed
* 0: all is fine
*/
-int test_dev_for_mkfs(char *file, int force_overwrite)
+int test_dev_for_mkfs(const char *file, int force_overwrite)
{
int ret, fd;
struct stat st;
@@ -2639,7 +2614,7 @@ int btrfs_scan_lblkid(void)
return 0;
}
-int is_vol_small(char *file)
+int is_vol_small(const char *file)
{
int fd = -1;
int e;
@@ -2673,7 +2648,7 @@ int is_vol_small(char *file)
* first whitespace delimited token is a case insensitive match with yes
* or y.
*/
-int ask_user(char *question)
+int ask_user(const char *question)
{
char buf[30] = {0,};
char *saveptr = NULL;
@@ -2789,17 +2764,6 @@ int test_minimum_size(const char *file, u32 nodesize)
return 0;
}
-/*
- * test if name is a correct subvolume name
- * this function return
- * 0-> name is not a correct subvolume name
- * 1-> name is a correct subvolume name
- */
-int test_issubvolname(const char *name)
-{
- return name[0] != '\0' && !strchr(name, '/') &&
- strcmp(name, ".") && strcmp(name, "..");
-}
/*
* Test if path is a directory
@@ -2855,7 +2819,7 @@ int find_next_key(struct btrfs_path *path, struct btrfs_key *key)
return 1;
}
-char* btrfs_group_type_str(u64 flag)
+const char* btrfs_group_type_str(u64 flag)
{
u64 mask = BTRFS_BLOCK_GROUP_TYPE_MASK |
BTRFS_SPACE_INFO_GLOBAL_RSV;
@@ -2876,7 +2840,7 @@ char* btrfs_group_type_str(u64 flag)
}
}
-char* btrfs_group_profile_str(u64 flag)
+const char* btrfs_group_profile_str(u64 flag)
{
switch (flag & BTRFS_BLOCK_GROUP_PROFILE_MASK) {
case 0:
@@ -2898,7 +2862,7 @@ char* btrfs_group_profile_str(u64 flag)
}
}
-u64 disk_size(char *path)
+u64 disk_size(const char *path)
{
struct statfs sfs;
@@ -2908,7 +2872,7 @@ u64 disk_size(char *path)
return sfs.f_bsize * sfs.f_blocks;
}
-u64 get_partition_size(char *dev)
+u64 get_partition_size(const char *dev)
{
u64 result;
int fd = open(dev, O_RDONLY);
@@ -3005,7 +2969,7 @@ int arg_copy_path(char *dest, const char *src, int destlen)
if (len >= PATH_MAX || len >= destlen)
return -ENAMETOOLONG;
- __strncpy__null(dest, src, destlen);
+ __strncpy_null(dest, src, destlen);
return 0;
}
@@ -3017,6 +2981,9 @@ unsigned int get_unit_mode_from_arg(int *argc, char *argv[], int df_mode)
int arg_end;
for (arg_i = 0; arg_i < *argc; arg_i++) {
+ if (!strcmp(argv[arg_i], "--"))
+ break;
+
if (!strcmp(argv[arg_i], "--raw")) {
unit_mode = UNITS_RAW;
argv[arg_i] = NULL;
@@ -3148,3 +3115,128 @@ void clean_args_no_options(int argc, char *argv[], const char * const *usagestr)
}
}
}
+
+/* Subvolume helper functions */
+/*
+ * test if name is a correct subvolume name
+ * this function return
+ * 0-> name is not a correct subvolume name
+ * 1-> name is a correct subvolume name
+ */
+int test_issubvolname(const char *name)
+{
+ return name[0] != '\0' && !strchr(name, '/') &&
+ strcmp(name, ".") && strcmp(name, "..");
+}
+
+/*
+ * Test if path is a subvolume
+ * Returns:
+ * 0 - path exists but it is not a subvolume
+ * 1 - path exists and it is a subvolume
+ * < 0 - error
+ */
+int test_issubvolume(const char *path)
+{
+ struct stat st;
+ struct statfs stfs;
+ int res;
+
+ res = stat(path, &st);
+ if (res < 0)
+ return -errno;
+
+ if (st.st_ino != BTRFS_FIRST_FREE_OBJECTID || !S_ISDIR(st.st_mode))
+ return 0;
+
+ res = statfs(path, &stfs);
+ if (res < 0)
+ return -errno;
+
+ return (int)stfs.f_type == BTRFS_SUPER_MAGIC;
+}
+
+const char *subvol_strip_mountpoint(const char *mnt, const char *full_path)
+{
+ int len = strlen(mnt);
+ if (!len)
+ return full_path;
+
+ if (mnt[len - 1] != '/')
+ len += 1;
+
+ return full_path + len;
+}
+
+/*
+ * Returns
+ * <0: Std error
+ * 0: All fine
+ * 1: Error; and error info printed to the terminal. Fixme.
+ * 2: If the fullpath is root tree instead of subvol tree
+ */
+int get_subvol_info(const char *fullpath, struct root_info *get_ri)
+{
+ u64 sv_id;
+ int ret = 1;
+ int fd = -1;
+ int mntfd = -1;
+ char *mnt = NULL;
+ const char *svpath = NULL;
+ DIR *dirstream1 = NULL;
+ DIR *dirstream2 = NULL;
+
+ ret = test_issubvolume(fullpath);
+ if (ret < 0)
+ return ret;
+ if (!ret) {
+ error("not a subvolume: %s", fullpath);
+ return 1;
+ }
+
+ ret = find_mount_root(fullpath, &mnt);
+ if (ret < 0)
+ return ret;
+ if (ret > 0) {
+ error("%s doesn't belong to btrfs mount point", fullpath);
+ return 1;
+ }
+ ret = 1;
+ svpath = subvol_strip_mountpoint(mnt, fullpath);
+
+ fd = btrfs_open_dir(fullpath, &dirstream1, 1);
+ if (fd < 0)
+ goto out;
+
+ ret = btrfs_list_get_path_rootid(fd, &sv_id);
+ if (ret) {
+ error("can't get rootid for '%s'", fullpath);
+ goto out;
+ }
+
+ mntfd = btrfs_open_dir(mnt, &dirstream2, 1);
+ if (mntfd < 0)
+ goto out;
+
+ if (sv_id == BTRFS_FS_TREE_OBJECTID) {
+ ret = 2;
+ /*
+ * So that caller may decide if thats an error or just fine.
+ */
+ goto out;
+ }
+
+ memset(get_ri, 0, sizeof(*get_ri));
+ get_ri->root_id = sv_id;
+
+ ret = btrfs_get_subvol(mntfd, get_ri);
+ if (ret)
+ error("can't find '%s': %d", svpath, ret);
+
+out:
+ close_file_or_dir(mntfd, dirstream2);
+ close_file_or_dir(fd, dirstream1);
+ free(mnt);
+
+ return ret;
+}
diff --git a/utils.h b/utils.h
index d53357a2..7a392c4c 100644
--- a/utils.h
+++ b/utils.h
@@ -23,6 +23,8 @@
#include "ctree.h"
#include <dirent.h>
#include <stdarg.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)
@@ -124,8 +126,8 @@ struct btrfs_mkfs_config {
int make_btrfs(int fd, struct btrfs_mkfs_config *cfg);
int btrfs_make_root_dir(struct btrfs_trans_handle *trans,
struct btrfs_root *root, u64 objectid);
-int btrfs_prepare_device(int fd, char *file, int zero_end, u64 *block_count_ret,
- u64 max_block_count, int discard);
+int btrfs_prepare_device(int fd, const char *file, int zero_end,
+ u64 *block_count_ret, u64 max_block_count, int discard);
int btrfs_add_to_fsid(struct btrfs_trans_handle *trans,
struct btrfs_root *root, int fd, char *path,
u64 block_count, u32 io_width, u32 io_align,
@@ -158,7 +160,7 @@ int get_fs_info(char *path, struct btrfs_ioctl_fs_info_args *fi_args,
int get_label(const char *btrfs_dev, char *label);
int set_label(const char *btrfs_dev, const char *label);
-char *__strncpy__null(char *dest, const char *src, size_t n);
+char *__strncpy_null(char *dest, const char *src, size_t n);
int is_block_device(const char *file);
int is_mount_point(const char *file);
int check_arg_type(const char *input);
@@ -166,17 +168,17 @@ int open_path_or_dev_mnt(const char *path, DIR **dirstream, int verbose);
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(char *file, int force_overwrite);
+#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(char *file);
+int is_vol_small(const char *file);
int csum_tree_block(struct btrfs_root *root, struct extent_buffer *buf,
int verify);
-int ask_user(char *question);
+int ask_user(const char *question);
int lookup_ino_rootid(int fd, u64 *rootid);
int btrfs_scan_lblkid(void);
int get_btrfs_mount(const char *dev, char *mp, size_t mp_size);
@@ -184,17 +186,17 @@ int find_mount_root(const char *path, char **mount_root);
int get_device_info(int fd, u64 devid,
struct btrfs_ioctl_dev_info_args *di_args);
int test_uuid_unique(char *fs_uuid);
-u64 disk_size(char *path);
-int get_device_info(int fd, u64 devid,
- struct btrfs_ioctl_dev_info_args *di_args);
-u64 get_partition_size(char *dev);
-const char* group_type_str(u64 flags);
-const char* group_profile_str(u64 flags);
+u64 disk_size(const char *path);
+u64 get_partition_size(const char *dev);
-int test_minimum_size(const char *file, u32 leafsize);
+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
@@ -205,19 +207,19 @@ int test_isdir(const char *path);
* 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 leafsize)
+static inline u64 btrfs_min_global_blk_rsv_size(u32 nodesize)
{
- return leafsize << 10;
+ return nodesize << 10;
}
-static inline u64 btrfs_min_dev_size(u32 leafsize)
+static inline u64 btrfs_min_dev_size(u32 nodesize)
{
return 2 * (BTRFS_MKFS_SYSTEM_GROUP_SIZE +
- btrfs_min_global_blk_rsv_size(leafsize));
+ btrfs_min_global_blk_rsv_size(nodesize));
}
int find_next_key(struct btrfs_path *path, struct btrfs_key *key);
-char* btrfs_group_type_str(u64 flag);
-char* btrfs_group_profile_str(u64 flag);
+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.
diff --git a/version.sh b/version.sh
index 3ad00e3a..6a5d6594 100755
--- a/version.sh
+++ b/version.sh
@@ -6,7 +6,7 @@
# Copyright 2008, Oracle
# Released under the GNU GPLv2
-v="v4.4.1"
+v="v4.5.2"
opt=$1
diff --git a/volumes.c b/volumes.c
index cc3e6b7f..4d22db25 100644
--- a/volumes.c
+++ b/volumes.c
@@ -1810,8 +1810,6 @@ static int read_one_dev(struct btrfs_root *root,
device = btrfs_find_device(root, devid, dev_uuid, fs_uuid);
if (!device) {
- printk("warning devid %llu not found already\n",
- (unsigned long long)devid);
device = kzalloc(sizeof(*device), GFP_NOFS);
if (!device)
return -ENOMEM;