summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDimitri John Ledkov <dimitri.j.ledkov@linux.intel.com>2015-10-24 00:09:28 +0100
committerDimitri John Ledkov <dimitri.j.ledkov@linux.intel.com>2015-10-24 00:09:28 +0100
commitda6658718a64c2855497dec85430775b7ad5f601 (patch)
tree9359dab24c745ea5410bc0238e4dc34f27a40eb8
parentaeb9dfe76e4f258530a96e0f0706d74b8ddc908f (diff)
parent9170e8eb55a32275ff7219e26f9a870bed95b199 (diff)
Record btrfs-tools (4.2.2-1) in archive suite sid
-rw-r--r--Android.mk111
-rw-r--r--Documentation/Makefile.in1
-rw-r--r--Documentation/btrfs-inspect-internal.asciidoc9
-rw-r--r--Documentation/btrfs-mount.asciidoc58
-rw-r--r--Documentation/btrfs-qgroup.asciidoc34
-rw-r--r--Documentation/btrfs-replace.asciidoc5
-rw-r--r--Documentation/btrfs-select-super.asciidoc29
-rw-r--r--INSTALL33
-rw-r--r--Makefile.in13
-rw-r--r--androidcompat.h28
-rwxr-xr-xautogen.sh11
-rw-r--r--btrfs-calc-size.c2
-rw-r--r--btrfs-completion2
-rw-r--r--btrfs-convert.c40
-rw-r--r--btrfs-corrupt-block.c12
-rw-r--r--btrfs-find-root.c7
-rw-r--r--btrfs-image.c110
-rw-r--r--btrfs-list.c8
-rw-r--r--btrfs-map-logical.c2
-rw-r--r--btrfstune.c58
-rw-r--r--chunk-recover.c93
-rw-r--r--cmds-check.c57
-rw-r--r--cmds-device.c168
-rw-r--r--cmds-fi-usage.c73
-rw-r--r--cmds-filesystem.c203
-rw-r--r--cmds-inspect.c341
-rw-r--r--cmds-property.c44
-rw-r--r--cmds-qgroup.c120
-rw-r--r--cmds-receive.c4
-rw-r--r--cmds-replace.c63
-rw-r--r--cmds-rescue.c26
-rw-r--r--cmds-scrub.c38
-rw-r--r--cmds-send.c12
-rw-r--r--cmds-subvolume.c62
-rwxr-xr-xconfigure18
-rw-r--r--ctree.h9
-rw-r--r--debian/changelog6
-rw-r--r--dir-item.c8
-rw-r--r--disk-io.c41
-rw-r--r--extent-tree.c7
-rw-r--r--extent_io.c4
-rw-r--r--find-root.c9
-rw-r--r--free-space-cache.c24
-rw-r--r--kerncompat.h2
-rw-r--r--mkfs.c188
-rw-r--r--print-tree.c4
-rw-r--r--qgroup.c10
-rw-r--r--task-utils.c24
-rwxr-xr-xtests/clean-tests.sh9
-rw-r--r--tests/common62
-rwxr-xr-xtests/convert-tests.sh8
-rwxr-xr-xtests/fsck-tests.sh41
-rwxr-xr-xtests/fsck-tests/006-bad-root-items/test.sh4
-rwxr-xr-xtests/fsck-tests/012-leaf-corruption/test.sh5
-rwxr-xr-xtests/fsck-tests/013-extent-tree-rebuild/test.sh20
-rw-r--r--tests/fsck-tests/017-missing-all-file-extent/default_case.img.xzbin0 -> 1104 bytes
-rw-r--r--tests/fuzz-tests/images/bad-superblock-1.raw.xzbin0 -> 228 bytes
-rw-r--r--tests/fuzz-tests/images/bad-superblock-2.raw.xzbin0 -> 228 bytes
-rw-r--r--tests/fuzz-tests/images/bad-superblock-3.raw.xzbin0 -> 228 bytes
-rw-r--r--tests/fuzz-tests/images/bad-superblock.txt17
-rw-r--r--tests/fuzz-tests/images/bko-104131-fsck-oob-read.raw.xzbin0 -> 192 bytes
-rw-r--r--tests/fuzz-tests/images/bko-104131-fsck-oob-read.txt31
-rw-r--r--tests/fuzz-tests/images/bko-104141-fsck-exception.raw.xzbin0 -> 196 bytes
-rw-r--r--tests/fuzz-tests/images/bko-104141-fsck-exception.txt9
-rw-r--r--tests/fuzz-tests/images/bko-97191-btrfs-image.raw.txt137
-rw-r--r--tests/fuzz-tests/images/bko-97191-btrfs-image.raw.xzbin0 -> 7076 bytes
-rw-r--r--tests/fuzz-tests/images/bko-97271-btrfs-image.raw.txt54
-rw-r--r--tests/fuzz-tests/images/bko-97271-btrfs-image.raw.xzbin0 -> 6580 bytes
-rwxr-xr-xtests/misc-tests.sh8
-rwxr-xr-xtests/misc-tests/001-btrfstune-features/test.sh16
-rwxr-xr-xtests/misc-tests/002-uuid-rewrite/test.sh16
-rwxr-xr-xtests/misc-tests/003-zero-log/test.sh16
-rwxr-xr-xtests/misc-tests/004-shrink-fs/test.sh70
-rwxr-xr-xtests/misc-tests/005-convert-progress-thread-crash/test.sh14
-rwxr-xr-xtests/misc-tests/006-image-on-missing-device/test.sh78
-rwxr-xr-xtests/misc-tests/007-subvolume-sync/test.sh32
-rw-r--r--utils.c256
-rw-r--r--utils.h28
-rwxr-xr-xversion.sh2
-rw-r--r--volumes.c11
-rw-r--r--volumes.h11
81 files changed, 2291 insertions, 895 deletions
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..fe3209b
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,111 @@
+LOCAL_PATH:= $(call my-dir)
+
+#include $(call all-subdir-makefiles)
+
+CFLAGS := -g -O1 -Wall -D_FORTIFY_SOURCE=2 -include config.h \
+ -DBTRFS_FLAT_INCLUDES -D_XOPEN_SOURCE=700 -fno-strict-aliasing -fPIC
+
+LDFLAGS := -static -rdynamic
+
+LIBS := -luuid -lblkid -lz -llzo2 -L. -lpthread
+LIBBTRFS_LIBS := $(LIBS)
+
+STATIC_CFLAGS := $(CFLAGS) -ffunction-sections -fdata-sections
+STATIC_LDFLAGS := -static -Wl,--gc-sections
+STATIC_LIBS := -luuid -lblkid -luuid -lz -llzo2 -L. -pthread
+
+btrfs_shared_libraries := libext2_uuid \
+ libext2_blkid
+
+objects := ctree.c disk-io.c radix-tree.c extent-tree.c print-tree.c \
+ root-tree.c dir-item.c file-item.c inode-item.c inode-map.c \
+ extent-cache.c extent_io.c volumes.c utils.c repair.c \
+ qgroup.c raid6.c free-space-cache.c list_sort.c props.c \
+ ulist.c qgroup-verify.c backref.c string-table.c task-utils.c \
+ inode.c file.c find-root.c
+cmds_objects := cmds-subvolume.c cmds-filesystem.c cmds-device.c cmds-scrub.c \
+ cmds-inspect.c cmds-balance.c cmds-send.c cmds-receive.c \
+ cmds-quota.c cmds-qgroup.c cmds-replace.c cmds-check.c \
+ cmds-restore.c cmds-rescue.c chunk-recover.c super-recover.c \
+ cmds-property.c cmds-fi-usage.c
+libbtrfs_objects := send-stream.c send-utils.c rbtree.c btrfs-list.c crc32c.c \
+ uuid-tree.c utils-lib.c rbtree-utils.c
+libbtrfs_headers := send-stream.h send-utils.h send.h rbtree.h btrfs-list.h \
+ crc32c.h list.h kerncompat.h radix-tree.h extent-cache.h \
+ extent_io.h ioctl.h ctree.h btrfsck.h version.h
+TESTS := fsck-tests.sh convert-tests.sh
+blkid_objects := partition/ superblocks/ topology/
+
+
+# external/e2fsprogs/lib is needed for uuid/uuid.h
+common_C_INCLUDES := $(LOCAL_PATH) external/e2fsprogs/lib/ external/lzo/include/ external/zlib/
+
+#----------------------------------------------------------
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(libbtrfs_objects)
+LOCAL_CFLAGS := $(STATIC_CFLAGS)
+LOCAL_MODULE := libbtrfs
+LOCAL_C_INCLUDES := $(common_C_INCLUDES)
+include $(BUILD_STATIC_LIBRARY)
+
+#----------------------------------------------------------
+include $(CLEAR_VARS)
+LOCAL_MODULE := btrfs
+#LOCAL_FORCE_STATIC_EXECUTABLE := true
+LOCAL_SRC_FILES := \
+ $(objects) \
+ $(cmds_objects) \
+ btrfs.c \
+ help.c \
+
+LOCAL_C_INCLUDES := $(common_C_INCLUDES)
+LOCAL_CFLAGS := $(STATIC_CFLAGS)
+#LOCAL_LDLIBS := $(LIBBTRFS_LIBS)
+#LOCAL_LDFLAGS := $(STATIC_LDFLAGS)
+LOCAL_SHARED_LIBRARIES := $(btrfs_shared_libraries)
+LOCAL_STATIC_LIBRARIES := libbtrfs liblzo-static libz
+LOCAL_SYSTEM_SHARED_LIBRARIES := libc libcutils
+
+LOCAL_EXPORT_C_INCLUDES := $(common_C_INCLUDES)
+#LOCAL_MODULE_TAGS := optional
+include $(BUILD_EXECUTABLE)
+
+#----------------------------------------------------------
+include $(CLEAR_VARS)
+LOCAL_MODULE := mkfs.btrfs
+LOCAL_SRC_FILES := \
+ $(objects) \
+ mkfs.c
+
+LOCAL_C_INCLUDES := $(common_C_INCLUDES)
+LOCAL_CFLAGS := $(STATIC_CFLAGS)
+#LOCAL_LDLIBS := $(LIBBTRFS_LIBS)
+#LOCAL_LDFLAGS := $(STATIC_LDFLAGS)
+LOCAL_SHARED_LIBRARIES := $(btrfs_shared_libraries)
+LOCAL_STATIC_LIBRARIES := libbtrfs liblzo-static
+LOCAL_SYSTEM_SHARED_LIBRARIES := libc libcutils
+
+LOCAL_EXPORT_C_INCLUDES := $(common_C_INCLUDES)
+#LOCAL_MODULE_TAGS := optional
+include $(BUILD_EXECUTABLE)
+
+#---------------------------------------------------------------
+include $(CLEAR_VARS)
+LOCAL_MODULE := btrfstune
+LOCAL_SRC_FILES := \
+ $(objects) \
+ btrfstune.c
+
+LOCAL_C_INCLUDES := $(common_C_INCLUDES)
+LOCAL_CFLAGS := $(STATIC_CFLAGS)
+LOCAL_SHARED_LIBRARIES := $(btrfs_shared_libraries)
+#LOCAL_LDLIBS := $(LIBBTRFS_LIBS)
+#LOCAL_LDFLAGS := $(STATIC_LDFLAGS)
+LOCAL_SHARED_LIBRARIES := $(btrfs_shared_libraries)
+LOCAL_STATIC_LIBRARIES := libbtrfs liblzo-static
+LOCAL_SYSTEM_SHARED_LIBRARIES := libc libcutils
+
+LOCAL_EXPORT_C_INCLUDES := $(common_C_INCLUDES)
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_EXECUTABLE)
+#--------------------------------------------------------------
diff --git a/Documentation/Makefile.in b/Documentation/Makefile.in
index 0c8b5ba..b03eaf2 100644
--- a/Documentation/Makefile.in
+++ b/Documentation/Makefile.in
@@ -9,6 +9,7 @@ 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
MAN8_TXT += mkfs.btrfs.asciidoc
diff --git a/Documentation/btrfs-inspect-internal.asciidoc b/Documentation/btrfs-inspect-internal.asciidoc
index 9f6ffac..f3f915b 100644
--- a/Documentation/btrfs-inspect-internal.asciidoc
+++ b/Documentation/btrfs-inspect-internal.asciidoc
@@ -41,6 +41,15 @@ set inode container's size.
This is used to increase inode container's size in case it is
not enough to read all the resolved results. The max value one can set is 64k.
+*min-dev-size* [options] <path>::
+Return the minimum size the device can be shrunk to, without performing any
+resize operation.
++
+`Options`
++
+--id::::
+specify the device id to query, default is 1
+
*rootid* <path>::
For a given file or directory, return the containing tree root id. For a
subvolume return it's own tree id.
diff --git a/Documentation/btrfs-mount.asciidoc b/Documentation/btrfs-mount.asciidoc
index 8cf7a0b..39215a8 100644
--- a/Documentation/btrfs-mount.asciidoc
+++ b/Documentation/btrfs-mount.asciidoc
@@ -20,12 +20,17 @@ MOUNT OPTIONS
Default is 1MB.
*autodefrag*::
+*noautodefrag*::
+ (since: 3.0, default: off) +
Disable/enable auto defragmentation.
Auto defragmentation detects small random writes into files and queue
them up for the defrag process. Works best for small files;
Not well suited for large database workloads.
-*check_int*|*check_int_data*|*check_int_print_mask='value'*::
+*check_int*::
+*check_int_data*::
+*check_int_print_mask='value'*::
+ (since: 3.0, default: off) +
These debugging options control the behavior of the integrity checking
module (the BTRFS_FS_CHECK_INTEGRITY config option required). +
+
@@ -44,19 +49,25 @@ MOUNT OPTIONS
for more info.
*commit='seconds'*::
- Set the interval of periodic commit, 30 seconds by default. Higher
+ (since: 3.12, default: 30) +
+ Set the interval of periodic commit. Higher
values defer data being synced to permanent storage with obvious
consequences when the system crashes. The upper bound is not forced,
but a warning is printed if it's more than 300 seconds (5 minutes).
-*compress*|*compress='type'*|*compress-force*|*compress-force='type'*::
- Control BTRFS file data compression. Type may be specified as "zlib"
- "lzo" or "no" (for no compression, used for remounting). If no type
- is specified, zlib is used. If compress-force is specified,
+*compress*::
+*compress='type'*::
+*compress-force*::
+*compress-force='type'*::
+ (default: off) +
+ Control BTRFS file data compression. Type may be specified as 'zlib',
+ 'lzo' or 'no' (for no compression, used for remounting). If no type
+ is specified, 'zlib' is used. If compress-force is specified,
all files will be compressed, whether or not they compress well.
- If compression is enabled, nodatacow and nodatasum are disabled.
+ NOTE: If compression is enabled, 'nodatacow' and 'nodatasum' are disabled.
*degraded*::
+ (default: off) +
Allow mounts to continue with missing devices. A read-write mount may
fail with too many devices missing, for example if a stripe member
is completely missing.
@@ -67,6 +78,8 @@ MOUNT OPTIONS
setup as root. May be specified multiple times for multiple devices.
*discard*::
+*nodiscard*::
+ (default: off) +
Disable/enable discard mount option.
Discard issues frequent commands to let the block device reclaim space
freed by the filesystem.
@@ -76,14 +89,18 @@ MOUNT OPTIONS
initiate batch trims from userspace).
*enospc_debug*::
+ (default: off) +
Disable/enable debugging option to be more verbose in some ENOSPC conditions.
*fatal_errors='action'*::
+ (since: 3.4, default: bug) +
Action to take when encountering a fatal error. +
- "bug" - BUG() on a fatal error. This is the default. +
+ "bug" - BUG() on a fatal error. +
"panic" - panic() on a fatal error.
*flushoncommit*::
+*noflushoncommit*::
+ (default: on) +
The `flushoncommit` mount option forces any data dirtied by a write in a
prior transaction to commit as part of the current commit. This makes
the committed state a fully consistent view of the file system from the
@@ -92,10 +109,13 @@ MOUNT OPTIONS
created.
*inode_cache*::
+*noinode_cache*::
+ (since: 3.0, default: off) +
Enable free inode number caching. Defaults to off due to an overflow
problem when the free space crcs don't fit inside a single page.
*max_inline='bytes'*::
+ (default: min(8192, page size) )
Specify the maximum amount of space, in bytes, that can be inlined in
a metadata B-tree leaf. The value is specified in bytes, optionally
with a K, M, or G suffix, case insensitive. In practice, this value
@@ -106,48 +126,64 @@ MOUNT OPTIONS
Specify that 1 metadata chunk should be allocated after every
'value' data chunks. Off by default.
+*acl*::
*noacl*::
+ (default: on) +
Enable/disable support for Posix Access Control Lists (ACLs). See the
`acl`(5) manual page for more information about ACLs.
+*barrier*::
*nobarrier*::
+ (default: on) +
ensure that certain IOs make it through the device cache and are on
persistent storage. If disabled on a device with a volatile
(non-battery-backed) write-back cache, nobarrier option will lead to
filesystem corruption on a system crash or power loss.
+*datacow*::
*nodatacow*::
+ (default: on) +
Enable/disable data copy-on-write for newly created files.
Nodatacow implies nodatasum, and disables all compression.
+*datasum*::
*nodatasum*::
+ (default: on) +
Enable/disable data checksumming for newly created files.
Datasum implies datacow.
+*treelog*::
*notreelog*::
+ (default: on) +
Enable/disable the tree logging used for fsync and O_SYNC writes.
*recovery*::
+ (since: 3.2, default: off) +
Enable autorecovery attempts if a bad tree root is found at mount time.
Currently this scans a list of several previous tree roots and tries to
use the first readable.
*rescan_uuid_tree*::
+ (since: 3.12, default: off) +
Force check and rebuild procedure of the UUID tree. This should not
normally be needed.
*skip_balance*::
+ (since: 3.3, default: off) +
Skip automatic resume of interrupted balance operation after mount.
May be resumed with "btrfs balance resume."
*nospace_cache*::
+ (since: 3.2) +
Disable freespace cache loading without clearing the cache.
*clear_cache*::
Force clearing and rebuilding of the disk space cache if something
has gone wrong.
-*ssd*|*nossd*|*ssd_spread*::
+*ssd*::
+*nossd*::
+*ssd_spread*::
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
@@ -166,7 +202,8 @@ MOUNT OPTIONS
filesystem.
You can use "btrfs subvolume list" to see subvolume ID numbers.
-*subvolrootid='objectid' (deprecated)*::
+*subvolrootid='objectid'*::
+ (deprecated) +
Mount subvolume specified by 'objectid' rather than the root subvolume.
This allows mounting of subvolumes which are not in the root of the mounted
filesystem.
@@ -177,6 +214,7 @@ MOUNT OPTIONS
to the number of CPUs + 2, or 8, whichever is smaller.
*user_subvol_rm_allowed*::
+ (default: off) +
Allow subvolumes to be deleted by a non-root user. Use with caution.
FILE ATTRIBUTES
diff --git a/Documentation/btrfs-qgroup.asciidoc b/Documentation/btrfs-qgroup.asciidoc
index eadfe1c..57cf012 100644
--- a/Documentation/btrfs-qgroup.asciidoc
+++ b/Documentation/btrfs-qgroup.asciidoc
@@ -11,34 +11,46 @@ SYNOPSIS
DESCRIPTION
-----------
-*btrfs qgroup* is used to control quota group(qgroup) of a btrfs filesystem.
+*btrfs qgroup* is used to control quota group (qgroup) of a btrfs filesystem.
-NOTE: To use qgroup, it needs to enable quota first using *btrfs quota*
+NOTE: To use qgroup you need to enable quota first using *btrfs quota enable*
command.
WARNING: Qgroup is not stable yet and will impact performance in current mainline
-kernel(v3.14 so far).
+kernel (v3.14 so far).
QGROUP
------
-Quota group or qgroup in btrfs has its hierarchy like subvolume.
-One subvolume/snapshot can reach its quota limits if it consumes all the quota
-assigned to it or any of the parent qgroup(s).
+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.
-Also for snapshot, it consumes no quota initially since all its data
-shares with its parent, so only modification in snapshot consumes quota.
+The limit sare 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.
-Every subvolume/snapshot will have its own qgroup with id '0/<subvolume id>'
-upon creating, but can be later destroyed by *btrfs qgroup destroy* command.
+The qgroup identifiers conform to 'level/id' where level 0 is reserved to the
+qgroups associated with subvolumes. Such qgroups are created automatically.
+
+The qgroup hierarchy is built by commands *create* and *assign*.
NOTE: If the qgroup of a subvolume is destroyed, quota about the subvolume
will not be functional until qgroup '0/<subvolume id>' is created again.
SUBCOMMAND
----------
-*assign* <src> <dst> <path>::
+*assign* [options] <src> <dst> <path>::
Assign qgroup <src> as the child qgroup of <dst> in the btrfs filesystem
identified by <path>.
++
+`Options`
++
+--rescan::::
+Automatically schedule quota rescan if the new qgroup assignment leads to
+quota inconsistency.
+--no-rescan::::
+Explicitly ask not to do a rescan.
*create* <qgroupid> <path>::
Create a subvolume quota group.
diff --git a/Documentation/btrfs-replace.asciidoc b/Documentation/btrfs-replace.asciidoc
index 774d850..5a14a40 100644
--- a/Documentation/btrfs-replace.asciidoc
+++ b/Documentation/btrfs-replace.asciidoc
@@ -13,11 +13,6 @@ DESCRIPTION
-----------
*btrfs replace* is used to replace btrfs managed devices with other device.
-NOTE: this is not currently supported for RAID5/6 profiles and must use the
-device add/delete workaround.
-It is recommended to see `btrfs-device`(8) for more details about btrfs device
-management.
-
SUBCOMMAND
----------
*cancel* <mount_point>::
diff --git a/Documentation/btrfs-select-super.asciidoc b/Documentation/btrfs-select-super.asciidoc
new file mode 100644
index 0000000..a8d7ef0
--- /dev/null
+++ b/Documentation/btrfs-select-super.asciidoc
@@ -0,0 +1,29 @@
+btrfs-select-super(8)
+=====================
+
+NAME
+----
+btrfs-select-super - overwrite superblock with a backup
+
+SYNOPSIS
+--------
+*btrfs-select-super* -s number dev
+
+DESCRIPTION
+-----------
+*btrfs-select-super* destructively overwrites all copies of the superblock
+with a specified copy. This helps with certain cases of damage, especially
+when barriers were disabled during a power failure. You can find a valid
+copy of the superblock with *btrfs check -s*.
+
+The filesystem specified by `dev` must not be mounted.
+
+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
+
+SEE ALSO
+--------
+`btrfsck check`(8)
diff --git a/INSTALL b/INSTALL
index 9c33c34..85a839f 100644
--- a/INSTALL
+++ b/INSTALL
@@ -24,6 +24,10 @@ XATTR library should be provided by the standard C library or by
Please note that the package names may differ according to the distribution.
See https://btrfs.wiki.kernel.org/index.php/Btrfs_source_repositories#Dependencies .
+
+Building from sources
+---------------------
+
To build from git sources you need to generate the configure script using the
autotools:
@@ -42,7 +46,34 @@ Specific CFLAGS or LDFLAGS should be set like
$ CFLAGS=... LDFLAGS=... ./configure --prefix=/usr
-and not as arguments to make.
+and not as arguments to make. You can specify additional flags to build via
+variables EXTRA_CFLAGS and EXTRA_LDFLAGS that get appended to the predefined
+values of the respective variables.
+
+ $ make EXTRA_CFLAGS=-ggdb3
+
+The build utilizes autotools, dependencies for generating the configure
+scripts are:
+
+* autconf, autoheader
+* automake, aclocal
+* pkg-config
+
+
+Staticly built binaries
+-----------------------
+
+The makefiles are ready to let you build static binaries of the utilities. This
+may be handy in rescue environments. Your system has to provide static version
+of the libraries.
+
+$ make static
+$ make btrfs.static
+$ make btrfs-convert.static
+
+The resulting static binaries have the '.static' suffix, the intermediate object
+files do not conflict with the normal (dynamic) build.
+
References:
* https://btrfs.wiki.kernel.org
diff --git a/Makefile.in b/Makefile.in
index 8450ab3..514a76f 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -10,16 +10,19 @@ INSTALL = @INSTALL@
DISABLE_DOCUMENTATION = @DISABLE_DOCUMENTATION@
DISABLE_BTRFSCONVERT = @DISABLE_BTRFSCONVERT@
+EXTRA_CFLAGS :=
+EXTRA_LDFLAGS :=
+
# Common build flags
CFLAGS = @CFLAGS@ \
-include config.h \
-DBTRFS_FLAT_INCLUDES \
-D_XOPEN_SOURCE=700 \
-fno-strict-aliasing \
- -fPIC
+ -fPIC $(EXTRA_CFLAGS)
LDFLAGS = @LDFLAGS@ \
- -rdynamic
+ -rdynamic $(EXTRA_LDFLAGS)
LIBS = @UUID_LIBS@ @BLKID_LIBS@ @ZLIB_LIBS@ @LZO2_LIBS@ -L. -pthread
LIBBTRFS_LIBS = $(LIBS)
@@ -196,7 +199,7 @@ $(libs_shared): $(libbtrfs_objects) $(lib_links) send.h
$(libs_static): $(libbtrfs_objects)
@echo " [AR] $@"
- $(Q)$(AR) cru libbtrfs.a $(libbtrfs_objects)
+ $(Q)$(AR) cr libbtrfs.a $(libbtrfs_objects)
$(lib_links):
@echo " [LN] $@"
@@ -344,8 +347,10 @@ install: $(libs) $(progs_install) $(INSTALLDIRS)
install-static: $(progs_static) $(INSTALLDIRS)
for p in $(progs_static) ; do \
- $(INSTALL) -D -m755 $$p $(DESTDIR)$(bindir)/`basename $$p .static` ; \
+ $(INSTALL) -D -m755 $$p $(DESTDIR)$(bindir)/ ; \
done
+ # btrfsck is a link to btrfs in the src tree, make it so for installed file as well
+ $(LN_S) -f btrfs.static $(DESTDIR)$(bindir)/btrfsck.static
$(INSTALLDIRS):
@echo "Making install in $(patsubst install-%,%,$@)"
diff --git a/androidcompat.h b/androidcompat.h
new file mode 100644
index 0000000..eec76da
--- /dev/null
+++ b/androidcompat.h
@@ -0,0 +1,28 @@
+/*
+ * Compatibility layer for Android.
+ *
+ * Stub calls or alternate functions for pthreads.
+ */
+
+#ifndef __ANDROID_H__
+#define __ANDROID_H__
+
+#ifdef ANDROID
+
+#define pthread_setcanceltype(type, oldtype) (0)
+#define pthread_setcancelstate(state, oldstate) (0)
+
+#define pthread_cancel(ret) pthread_kill((ret), SIGUSR1)
+
+typedef struct blkid_struct_probe *blkid_probe;
+
+#include <dirent.h>
+#define direct dirent
+
+#else /* !ANDROID */
+
+#include <sys/dir.h>
+
+#endif /* !ANDROID */
+
+#endif /* __ANDROID_H__ */
diff --git a/autogen.sh b/autogen.sh
index b74e793..9669850 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -37,10 +37,19 @@ test -f btrfs.c || {
(automake --version) < /dev/null > /dev/null 2>&1 || {
echo
echo "You must have automake installed to generate btrfs-progs build system."
- echo
+ echo
DIE=1
}
+(pkg-config --version) < /dev/null > /dev/null 2>&1 || {
+ echo
+ echo "You must have pkg-config installed to use btrfs-progs build system."
+ echo "The pkg-config utility was not found in the standard location, set"
+ echo "the PKG_CONFIG/PKG_CONFIG_PATH/PKG_CONFIG_LIBDIR variables at the"
+ echo "configure time."
+ echo
+}
+
if test "$DIE" -eq 1; then
exit 1
fi
diff --git a/btrfs-calc-size.c b/btrfs-calc-size.c
index 88f92e1..7287858 100644
--- a/btrfs-calc-size.c
+++ b/btrfs-calc-size.c
@@ -421,7 +421,7 @@ out:
return ret;
}
-static void usage()
+static void usage(void)
{
fprintf(stderr, "Usage: calc-size [-v] [-b] <device>\n");
}
diff --git a/btrfs-completion b/btrfs-completion
index 884d2e8..a34191b 100644
--- a/btrfs-completion
+++ b/btrfs-completion
@@ -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'
+ commands_inspect_internal='inode-resolve logical-resolve subvolid-resolve rootid min-dev-size'
commands_property='get set list'
commands_quota='enable disable rescan'
commands_qgroup='assign remove create destroy show limit'
diff --git a/btrfs-convert.c b/btrfs-convert.c
index b89c685..802930c 100644
--- a/btrfs-convert.c
+++ b/btrfs-convert.c
@@ -71,10 +71,8 @@ static void *print_copied_inodes(void *p)
static int after_copied_inodes(void *p)
{
- struct task_ctx *priv = p;
-
printf("\n");
- task_period_stop(priv->info);
+ fflush(stdout);
return 0;
}
@@ -207,7 +205,8 @@ static int cache_free_extents(struct btrfs_root *root, ext2_filsys ext2_fs)
}
static int custom_alloc_extent(struct btrfs_root *root, u64 num_bytes,
- u64 hint_byte, struct btrfs_key *ins)
+ u64 hint_byte, struct btrfs_key *ins,
+ int metadata)
{
u64 start;
u64 end;
@@ -246,6 +245,14 @@ static int custom_alloc_extent(struct btrfs_root *root, u64 num_bytes,
continue;
}
+ if (metadata) {
+ BUG_ON(num_bytes != root->nodesize);
+ if (check_crossing_stripes(start, num_bytes)) {
+ last = round_down(start + num_bytes,
+ BTRFS_STRIPE_LEN);
+ continue;
+ }
+ }
clear_extent_dirty(&root->fs_info->free_space_cache,
start, start + num_bytes - 1, 0);
@@ -1009,6 +1016,8 @@ static int copy_inode_item(struct btrfs_inode_item *dst,
struct ext2_inode *src, u32 blocksize)
{
btrfs_set_stack_inode_generation(dst, 1);
+ btrfs_set_stack_inode_sequence(dst, 0);
+ btrfs_set_stack_inode_transid(dst, 1);
btrfs_set_stack_inode_size(dst, src->i_size);
btrfs_set_stack_inode_nbytes(dst, 0);
btrfs_set_stack_inode_block_group(dst, 0);
@@ -1045,6 +1054,8 @@ static int copy_inode_item(struct btrfs_inode_item *dst,
new_decode_dev(src->i_block[1]));
}
}
+ memset(&dst->reserved, 0, sizeof(dst->reserved));
+
return 0;
}
@@ -1175,6 +1186,7 @@ static int copy_inodes(struct btrfs_root *root, ext2_filsys ext2_fs,
}
ret = btrfs_commit_transaction(trans, root);
BUG_ON(ret);
+ ext2fs_close_inode_scan(ext2_scan);
return ret;
}
@@ -1280,7 +1292,7 @@ static int create_ext2_image(struct btrfs_root *root, ext2_filsys ext2_fs,
* special, we can't rely on relocate_extents_range to relocate it.
*/
for (last_byte = 0; last_byte < first_free; last_byte += sectorsize) {
- ret = custom_alloc_extent(root, sectorsize, 0, &key);
+ ret = custom_alloc_extent(root, sectorsize, 0, &key, 0);
if (ret)
goto fail;
ret = copy_disk_extent(root, key.objectid, last_byte,
@@ -1938,7 +1950,7 @@ static int relocate_one_reference(struct btrfs_trans_handle *trans,
ret = get_state_private(reloc_tree, bytenr, &new_pos);
BUG_ON(ret);
} else {
- ret = custom_alloc_extent(root, sectorsize, 0, &key);
+ ret = custom_alloc_extent(root, sectorsize, 0, &key, 0);
if (ret)
goto fail;
new_pos = key.objectid;
@@ -2307,7 +2319,7 @@ static int do_convert(const char *devname, int datacsum, int packing, int noxatt
fprintf(stderr, "filetype feature is missing\n");
goto fail;
}
- if (btrfs_check_nodesize(nodesize, blocksize))
+ if (btrfs_check_nodesize(nodesize, blocksize, features))
goto fail;
blocks_per_node = nodesize / blocksize;
ret = -blocks_per_node;
@@ -2421,7 +2433,7 @@ static int do_convert(const char *devname, int datacsum, int packing, int noxatt
fprintf(stderr, "copy label '%s'\n",
root->fs_info->super_copy->label);
} else if (copylabel == -1) {
- strncpy(root->fs_info->super_copy->label, fslabel, BTRFS_LABEL_SIZE);
+ strcpy(root->fs_info->super_copy->label, fslabel);
fprintf(stderr, "set label to '%s'\n", fslabel);
}
@@ -2861,7 +2873,7 @@ int main(int argc, char *argv[])
int usage_error = 0;
int progress = 1;
char *file;
- char *fslabel = NULL;
+ char fslabel[BTRFS_LABEL_SIZE];
u64 features = BTRFS_MKFS_DEFAULT_FEATURES;
while(1) {
@@ -2903,13 +2915,13 @@ int main(int argc, char *argv[])
break;
case 'l':
copylabel = -1;
- fslabel = strdup(optarg);
- if (strlen(fslabel) > BTRFS_LABEL_SIZE) {
+ if (strlen(optarg) >= BTRFS_LABEL_SIZE) {
fprintf(stderr,
- "warning: label too long, trimmed to %d bytes\n",
- BTRFS_LABEL_SIZE);
- fslabel[BTRFS_LABEL_SIZE] = 0;
+ "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;
break;
case 'L':
copylabel = 1;
diff --git a/btrfs-corrupt-block.c b/btrfs-corrupt-block.c
index 1a2aa23..ee2093f 100644
--- a/btrfs-corrupt-block.c
+++ b/btrfs-corrupt-block.c
@@ -33,8 +33,8 @@
#define FIELD_BUF_LEN 80
-struct extent_buffer *debug_corrupt_block(struct btrfs_root *root, u64 bytenr,
- u32 blocksize, u64 copy)
+static struct extent_buffer *debug_corrupt_block(struct btrfs_root *root,
+ u64 bytenr, u32 blocksize, u64 copy)
{
int ret;
struct extent_buffer *eb;
@@ -880,7 +880,7 @@ static int delete_csum(struct btrfs_root *root, u64 bytenr, u64 bytes)
* If using COW, chunk recover will use the old item to recover,
* which is still OK but we want to check the ability to rebuild chunk
* not only restore the old ones */
-int corrupt_item_nocow(struct btrfs_trans_handle *trans,
+static int corrupt_item_nocow(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct btrfs_path *path,
int del)
{
@@ -913,7 +913,7 @@ int corrupt_item_nocow(struct btrfs_trans_handle *trans,
}
return ret;
}
-int corrupt_chunk_tree(struct btrfs_trans_handle *trans,
+static int corrupt_chunk_tree(struct btrfs_trans_handle *trans,
struct btrfs_root *root)
{
int ret;
@@ -986,7 +986,7 @@ free_out:
btrfs_free_path(path);
return ret;
}
-int find_chunk_offset(struct btrfs_root *root,
+static int find_chunk_offset(struct btrfs_root *root,
struct btrfs_path *path, u64 offset)
{
struct btrfs_key key;
@@ -1010,7 +1010,7 @@ int find_chunk_offset(struct btrfs_root *root,
goto out;
}
if (ret < 0) {
- fprintf(stderr, "Error searching chunk");
+ fprintf(stderr, "Error searching chunk\n");
goto out;
}
out:
diff --git a/btrfs-find-root.c b/btrfs-find-root.c
index 1cb3085..01b3603 100644
--- a/btrfs-find-root.c
+++ b/btrfs-find-root.c
@@ -109,6 +109,9 @@ static void print_one_result(struct cache_extent *tree_block,
tree_block->start, generation, level);
if (unsure)
printf("but we are unsure about the correct generation/level\n");
+ else if (level == filter->match_level &&
+ generation == filter->match_gen)
+ printf("and it matches superblock\n");
else
printf("but generation/level doesn't match, want gen: %llu level: %u\n",
filter->match_gen, filter->match_level);
@@ -129,8 +132,10 @@ static void print_find_root_result(struct cache_tree *result,
struct btrfs_find_root_gen_cache, cache);
level = gen_cache->highest_level;
generation = cache->start;
+ /* For exact found one, skip it as it's output before */
if (level == filter->match_level &&
- generation == filter->match_gen)
+ generation == filter->match_gen &&
+ !filter->search_all)
continue;
for (tree_block = last_cache_extent(&gen_cache->eb_tree);
tree_block; tree_block = prev_cache_extent(tree_block))
diff --git a/btrfs-image.c b/btrfs-image.c
index 3684a05..82eed05 100644
--- a/btrfs-image.c
+++ b/btrfs-image.c
@@ -872,54 +872,34 @@ out:
static int read_data_extent(struct metadump_struct *md,
struct async_work *async)
{
- struct btrfs_multi_bio *multi = NULL;
- struct btrfs_device *device;
+ struct btrfs_root *root = md->root;
u64 bytes_left = async->size;
u64 logical = async->start;
u64 offset = 0;
- u64 bytenr;
u64 read_len;
- ssize_t done;
- int fd;
+ int num_copies;
+ int cur_mirror;
int ret;
- while (bytes_left) {
- read_len = bytes_left;
- ret = btrfs_map_block(&md->root->fs_info->mapping_tree, READ,
- logical, &read_len, &multi, 0, NULL);
- if (ret) {
- fprintf(stderr, "Couldn't map data block %d\n", ret);
- return ret;
- }
-
- device = multi->stripes[0].dev;
+ num_copies = btrfs_num_copies(&root->fs_info->mapping_tree, logical,
+ bytes_left);
- if (device->fd == 0) {
- fprintf(stderr,
- "Device we need to read from is not open\n");
- free(multi);
- return -EIO;
- }
- fd = device->fd;
- bytenr = multi->stripes[0].physical;
- free(multi);
-
- read_len = min(read_len, bytes_left);
- done = pread64(fd, async->buffer+offset, read_len, bytenr);
- if (done < read_len) {
- if (done < 0)
- fprintf(stderr, "Error reading extent %d\n",
- errno);
- else
- fprintf(stderr, "Short read\n");
- return -EIO;
+ /* Try our best to read data, just like read_tree_block() */
+ for (cur_mirror = 0; cur_mirror < num_copies; cur_mirror++) {
+ while (bytes_left) {
+ read_len = bytes_left;
+ ret = read_extent_data(root,
+ (char *)(async->buffer + offset),
+ logical, &read_len, cur_mirror);
+ if (ret < 0)
+ break;
+ offset += read_len;
+ logical += read_len;
+ bytes_left -= read_len;
}
-
- bytes_left -= done;
- offset += done;
- logical += done;
}
-
+ if (bytes_left)
+ return -EIO;
return 0;
}
@@ -2608,8 +2588,8 @@ static int update_disk_super_on_device(struct btrfs_fs_info *info,
char fs_uuid[BTRFS_UUID_SIZE];
u64 devid, type, io_align, io_width;
u64 sector_size, total_bytes, bytes_used;
- char *buf;
- int fp;
+ char buf[BTRFS_SUPER_INFO_SIZE];
+ int fp = -1;
int ret;
key.objectid = BTRFS_DEV_ITEMS_OBJECTID;
@@ -2619,8 +2599,9 @@ static int update_disk_super_on_device(struct btrfs_fs_info *info,
btrfs_init_path(&path);
ret = btrfs_search_slot(NULL, info->chunk_root, &key, &path, 0, 0);
if (ret) {
- fprintf(stderr, "search key fails\n");
- exit(1);
+ fprintf(stderr, "ERROR: search key failed\n");
+ ret = -EIO;
+ goto out;
}
leaf = path.nodes[0];
@@ -2629,8 +2610,9 @@ static int update_disk_super_on_device(struct btrfs_fs_info *info,
devid = btrfs_device_id(leaf, dev_item);
if (devid != cur_devid) {
- printk("devid %llu mismatch with %llu\n", devid, cur_devid);
- exit(1);
+ printk("ERROR: devid %llu mismatch with %llu\n", devid, cur_devid);
+ ret = -EIO;
+ goto out;
}
type = btrfs_device_type(leaf, dev_item);
@@ -2649,15 +2631,9 @@ static int update_disk_super_on_device(struct btrfs_fs_info *info,
/* update other devices' super block */
fp = open(other_dev, O_CREAT | O_RDWR, 0600);
if (fp < 0) {
- fprintf(stderr, "could not open %s\n", other_dev);
- exit(1);
- }
-
- buf = malloc(BTRFS_SUPER_INFO_SIZE);
- if (!buf) {
- ret = -ENOMEM;
- close(fp);
- return ret;
+ fprintf(stderr, "ERROR: could not open %s\n", other_dev);
+ ret = -EIO;
+ goto out;
}
memcpy(buf, info->super_copy, BTRFS_SUPER_INFO_SIZE);
@@ -2678,6 +2654,10 @@ static int update_disk_super_on_device(struct btrfs_fs_info *info,
ret = pwrite64(fp, buf, BTRFS_SUPER_INFO_SIZE, BTRFS_SUPER_INFO_OFFSET);
if (ret != BTRFS_SUPER_INFO_SIZE) {
+ if (ret < 0)
+ fprintf(stderr, "ERROR: cannot write superblock: %s\n", strerror(ret));
+ else
+ fprintf(stderr, "ERROR: cannot write superblock\n");
ret = -EIO;
goto out;
}
@@ -2685,9 +2665,9 @@ static int update_disk_super_on_device(struct btrfs_fs_info *info,
write_backup_supers(fp, (u8 *)buf);
out:
- free(buf);
- close(fp);
- return 0;
+ if (fp != -1)
+ close(fp);
+ return ret;
}
static void print_usage(int ret)
@@ -2710,7 +2690,7 @@ int main(int argc, char *argv[])
{
char *source;
char *target;
- u64 num_threads = 1;
+ u64 num_threads = 0;
u64 compress_level = 0;
int create = 1;
int old_restore = 0;
@@ -2806,10 +2786,14 @@ int main(int argc, char *argv[])
}
}
- if (num_threads == 1 && compress_level > 0) {
- num_threads = sysconf(_SC_NPROCESSORS_ONLN);
- if (num_threads <= 0)
- num_threads = 1;
+ if (compress_level > 0 || create == 0) {
+ if (num_threads == 0) {
+ num_threads = sysconf(_SC_NPROCESSORS_ONLN);
+ if (num_threads <= 0)
+ num_threads = 1;
+ }
+ } else {
+ num_threads = 0;
}
if (create) {
@@ -2897,5 +2881,7 @@ out:
}
}
+ btrfs_close_all_devices();
+
return !!ret;
}
diff --git a/btrfs-list.c b/btrfs-list.c
index 875a89d..d54de61 100644
--- a/btrfs-list.c
+++ b/btrfs-list.c
@@ -254,11 +254,15 @@ static int btrfs_list_setup_comparer(struct btrfs_list_comparer_set **comp_set,
BUG_ON(set->ncomps > set->total);
if (set->ncomps == set->total) {
+ void *tmp;
+
size = set->total + BTRFS_LIST_NCOMPS_INCREASE;
size = sizeof(*set) + size * sizeof(struct btrfs_list_comparer);
+ tmp = set;
set = realloc(set, size);
if (!set) {
fprintf(stderr, "memory allocation failed\n");
+ free(tmp);
exit(1);
}
@@ -1232,11 +1236,15 @@ int btrfs_list_setup_filter(struct btrfs_list_filter_set **filter_set,
BUG_ON(set->nfilters > set->total);
if (set->nfilters == set->total) {
+ void *tmp;
+
size = set->total + BTRFS_LIST_NFILTERS_INCREASE;
size = sizeof(*set) + size * sizeof(struct btrfs_list_filter);
+ tmp = set;
set = realloc(set, size);
if (!set) {
fprintf(stderr, "memory allocation failed\n");
+ free(tmp);
exit(1);
}
diff --git a/btrfs-map-logical.c b/btrfs-map-logical.c
index a88e56e..d9fa6b2 100644
--- a/btrfs-map-logical.c
+++ b/btrfs-map-logical.c
@@ -262,6 +262,7 @@ int main(int ac, char **av)
root = open_ctree(dev, 0, 0);
if (!root) {
fprintf(stderr, "Open ctree failed\n");
+ free(output_file);
exit(1);
}
@@ -354,6 +355,7 @@ out_close_fd:
if (output_file && out_fd != 1)
close(out_fd);
close:
+ free(output_file);
close_ctree(root);
if (ret < 0)
ret = 1;
diff --git a/btrfstune.c b/btrfstune.c
index 6f07f31..c248ee6 100644
--- a/btrfstune.c
+++ b/btrfstune.c
@@ -95,17 +95,12 @@ static int change_header_uuid(struct btrfs_root *root, struct extent_buffer *eb)
int same_chunk_tree_uuid = 1;
int ret;
- /* Check for whether we need to change fs/chunk id */
- if (!fs_info->new_fsid && !fs_info->new_chunk_tree_uuid)
- return 0;
- if (fs_info->new_fsid)
- same_fsid = !memcmp_extent_buffer(eb, fs_info->new_fsid,
- btrfs_header_fsid(), BTRFS_FSID_SIZE);
- if (fs_info->new_chunk_tree_uuid)
- same_chunk_tree_uuid =
- !memcmp_extent_buffer(eb, fs_info->new_chunk_tree_uuid,
- btrfs_header_chunk_tree_uuid(eb),
- BTRFS_UUID_SIZE);
+ same_fsid = !memcmp_extent_buffer(eb, fs_info->new_fsid,
+ btrfs_header_fsid(), BTRFS_FSID_SIZE);
+ same_chunk_tree_uuid =
+ !memcmp_extent_buffer(eb, fs_info->new_chunk_tree_uuid,
+ btrfs_header_chunk_tree_uuid(eb),
+ BTRFS_UUID_SIZE);
if (same_fsid && same_chunk_tree_uuid)
return 0;
if (!same_fsid)
@@ -127,9 +122,6 @@ static int change_extents_uuid(struct btrfs_fs_info *fs_info)
struct btrfs_key key = {0, 0, 0};
int ret = 0;
- if (!fs_info->new_fsid && !fs_info->new_chunk_tree_uuid)
- return 0;
-
path = btrfs_alloc_path();
if (!path)
return -ENOMEM;
@@ -196,16 +188,16 @@ static int change_device_uuid(struct btrfs_root *root, struct extent_buffer *eb,
int ret = 0;
di = btrfs_item_ptr(eb, slot, struct btrfs_dev_item);
- if (fs_info->new_fsid) {
- if (!memcmp_extent_buffer(eb, fs_info->new_fsid,
- (unsigned long)btrfs_device_fsid(di),
- BTRFS_FSID_SIZE))
- return ret;
- write_extent_buffer(eb, fs_info->new_fsid,
- (unsigned long)btrfs_device_fsid(di),
- BTRFS_FSID_SIZE);
- ret = write_tree_block(NULL, root, eb);
- }
+ if (!memcmp_extent_buffer(eb, fs_info->new_fsid,
+ (unsigned long)btrfs_device_fsid(di),
+ BTRFS_FSID_SIZE))
+ return ret;
+
+ write_extent_buffer(eb, fs_info->new_fsid,
+ (unsigned long)btrfs_device_fsid(di),
+ BTRFS_FSID_SIZE);
+ ret = write_tree_block(NULL, root, eb);
+
return ret;
}
@@ -216,12 +208,6 @@ static int change_devices_uuid(struct btrfs_fs_info *fs_info)
struct btrfs_key key = {0, 0, 0};
int ret = 0;
- /*
- * Unlike change_extents_uuid, we only need to change fsid in dev_item
- */
- if (!fs_info->new_fsid)
- return 0;
-
path = btrfs_alloc_path();
if (!path)
return -ENOMEM;
@@ -258,11 +244,7 @@ static int change_fsid_prepare(struct btrfs_fs_info *fs_info)
u64 flags = btrfs_super_flags(fs_info->super_copy);
int ret = 0;
- if (!fs_info->new_fsid && !fs_info->new_chunk_tree_uuid)
- return 0;
-
- if (fs_info->new_fsid)
- flags |= BTRFS_SUPER_FLAG_CHANGING_FSID;
+ flags |= BTRFS_SUPER_FLAG_CHANGING_FSID;
btrfs_set_super_flags(fs_info->super_copy, flags);
memcpy(fs_info->super_copy->fsid, fs_info->new_fsid, BTRFS_FSID_SIZE);
@@ -281,11 +263,7 @@ static int change_fsid_done(struct btrfs_fs_info *fs_info)
{
u64 flags = btrfs_super_flags(fs_info->super_copy);
- if (!fs_info->new_fsid && !fs_info->new_chunk_tree_uuid)
- return 0;
-
- if (fs_info->new_fsid)
- flags &= ~BTRFS_SUPER_FLAG_CHANGING_FSID;
+ flags &= ~BTRFS_SUPER_FLAG_CHANGING_FSID;
btrfs_set_super_flags(fs_info->super_copy, flags);
return write_all_supers(fs_info->tree_root);
diff --git a/chunk-recover.c b/chunk-recover.c
index 832b3b1..1fb04f7 100644
--- a/chunk-recover.c
+++ b/chunk-recover.c
@@ -16,6 +16,9 @@
* Boston, MA 021110-1307, USA.
*/
+#include "kerncompat.h"
+#include "androidcompat.h"
+
#include <stdio.h>
#include <stdio_ext.h>
#include <stdlib.h>
@@ -26,7 +29,6 @@
#include <uuid/uuid.h>
#include <pthread.h>
-#include "kerncompat.h"
#include "list.h"
#include "radix-tree.h"
#include "ctree.h"
@@ -76,6 +78,7 @@ struct device_scan {
struct recover_control *rc;
struct btrfs_device *dev;
int fd;
+ u64 bytenr;
};
static struct extent_record *btrfs_new_extent_record(struct extent_buffer *eb)
@@ -258,7 +261,7 @@ again:
list_del_init(&exist->list);
free(exist);
/*
- * We must do seach again to avoid the following cache.
+ * We must do search again to avoid the following cache.
* /--old bg 1--//--old bg 2--/
* /--new bg--/
*/
@@ -766,6 +769,8 @@ static int scan_one_device(void *dev_scan_struct)
bytenr = 0;
while (1) {
+ dev_scan->bytenr = bytenr;
+
if (is_super_block_address(bytenr))
bytenr += rc->sectorsize;
@@ -829,12 +834,11 @@ static int scan_devices(struct recover_control *rc)
struct btrfs_device *dev;
struct device_scan *dev_scans;
pthread_t *t_scans;
- int *t_rets;
+ long *t_rets;
int devnr = 0;
int devidx = 0;
- int cancel_from = 0;
- int cancel_to = 0;
int i;
+ int all_done;
list_for_each_entry(dev, &rc->fs_devices->devices, dev_list)
devnr++;
@@ -845,7 +849,7 @@ static int scan_devices(struct recover_control *rc)
t_scans = (pthread_t *)malloc(sizeof(pthread_t) * devnr);
if (!t_scans)
return -ENOMEM;
- t_rets = (int *)malloc(sizeof(int) * devnr);
+ t_rets = (long *)malloc(sizeof(long) * devnr);
if (!t_rets)
return -ENOMEM;
@@ -860,32 +864,64 @@ static int scan_devices(struct recover_control *rc)
dev_scans[devidx].rc = rc;
dev_scans[devidx].dev = dev;
dev_scans[devidx].fd = fd;
- ret = pthread_create(&t_scans[devidx], NULL,
- (void *)scan_one_device,
- (void *)&dev_scans[devidx]);
- if (ret) {
- cancel_from = 0;
- cancel_to = devidx - 1;
- goto out1;
- }
+ dev_scans[devidx].bytenr = -1;
devidx++;
}
- i = 0;
- while (i < devidx) {
- ret = pthread_join(t_scans[i], (void **)&t_rets[i]);
- if (ret || t_rets[i]) {
- ret = 1;
- cancel_from = i + 1;
- cancel_to = devnr - 1;
+ for (i = 0; i < devidx; i++) {
+ ret = pthread_create(&t_scans[i], NULL,
+ (void *)scan_one_device,
+ (void *)&dev_scans[i]);
+ if (ret)
goto out1;
+
+ dev_scans[i].bytenr = 0;
+ }
+
+ while (1) {
+ all_done = 1;
+ for (i = 0; i < devidx; i++) {
+ if (dev_scans[i].bytenr == -1)
+ continue;
+ ret = pthread_tryjoin_np(t_scans[i],
+ (void **)&t_rets[i]);
+ if (ret == EBUSY) {
+ all_done = 0;
+ continue;
+ }
+ if (ret || t_rets[i]) {
+ ret = 1;
+ goto out1;
+ }
+ dev_scans[i].bytenr = -1;
+ }
+
+ printf("\rScanning: ");
+ for (i = 0; i < devidx; i++) {
+ if (dev_scans[i].bytenr == -1)
+ printf("%sDONE in dev%d",
+ i ? ", " : "", i);
+ else
+ printf("%s%llu in dev%d",
+ i ? ", " : "", dev_scans[i].bytenr, i);
}
- i++;
+ /* clear chars if exist in tail */
+ printf(" ");
+ printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b");
+ fflush(stdout);
+
+ if (all_done) {
+ printf("\n");
+ break;
+ }
+
+ sleep(1);
}
out1:
- while (ret && (cancel_from <= cancel_to)) {
- pthread_cancel(t_scans[cancel_from]);
- cancel_from++;
+ for (i = 0; i < devidx; i++) {
+ if (dev_scans[i].bytenr == -1)
+ continue;
+ pthread_cancel(t_scans[i]);
}
out2:
free(dev_scans);
@@ -1058,8 +1094,7 @@ err:
return ret;
}
-static int block_group_free_all_extent(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
+static int block_group_free_all_extent(struct btrfs_root *root,
struct block_group_record *bg)
{
struct btrfs_block_group_cache *cache;
@@ -1099,7 +1134,7 @@ static int remove_chunk_extent_item(struct btrfs_trans_handle *trans,
if (ret)
return ret;
- ret = block_group_free_all_extent(trans, root, chunk->bg_rec);
+ ret = block_group_free_all_extent(root, chunk->bg_rec);
if (ret)
return ret;
}
@@ -2276,7 +2311,7 @@ static void validate_rebuild_chunks(struct recover_control *rc)
}
/*
- * Return 0 when succesful, < 0 on error and > 0 if aborted by user
+ * Return 0 when successful, < 0 on error and > 0 if aborted by user
*/
int btrfs_recover_chunk_tree(char *path, int verbose, int yes)
{
diff --git a/cmds-check.c b/cmds-check.c
index dd2fce3..4225b21 100644
--- a/cmds-check.c
+++ b/cmds-check.c
@@ -126,6 +126,7 @@ struct extent_record {
unsigned int is_root:1;
unsigned int metadata:1;
unsigned int bad_full_backref:1;
+ unsigned int crossing_stripes:1;
};
struct inode_backref {
@@ -185,7 +186,7 @@ static u64 first_extent_gap(struct rb_root *holes)
return hole->start;
}
-int compare_hole(struct rb_node *node1, struct rb_node *node2)
+static int compare_hole(struct rb_node *node1, struct rb_node *node2)
{
struct file_extent_hole *hole1;
struct file_extent_hole *hole2;
@@ -615,15 +616,20 @@ static void print_inode_error(struct btrfs_root *root, struct inode_record *rec)
if (errors & I_ERR_FILE_EXTENT_DISCOUNT) {
struct file_extent_hole *hole;
struct rb_node *node;
+ int found = 0;
node = rb_first(&rec->holes);
fprintf(stderr, "Found file extent holes:\n");
while (node) {
+ found = 1;
hole = rb_entry(node, struct file_extent_hole, node);
- fprintf(stderr, "\tstart: %llu, len:%llu\n",
+ fprintf(stderr, "\tstart: %llu, len: %llu\n",
hole->start, hole->len);
node = rb_next(node);
}
+ if (!found)
+ fprintf(stderr, "\tstart: 0, len: %llu\n",
+ round_up(rec->isize, root->sectorsize));
}
}
@@ -2398,7 +2404,7 @@ static int repair_inode_nlinks(struct btrfs_trans_handle *trans,
BTRFS_FIRST_FREE_OBJECTID, &lost_found_ino,
mode);
if (ret < 0) {
- fprintf(stderr, "Failed to create '%s' dir: %s",
+ fprintf(stderr, "Failed to create '%s' dir: %s\n",
dir_name, strerror(-ret));
goto out;
}
@@ -2426,7 +2432,7 @@ static int repair_inode_nlinks(struct btrfs_trans_handle *trans,
}
if (ret < 0) {
fprintf(stderr,
- "Failed to link the inode %llu to %s dir: %s",
+ "Failed to link the inode %llu to %s dir: %s\n",
rec->ino, dir_name, strerror(-ret));
goto out;
}
@@ -2659,11 +2665,13 @@ static int repair_inode_discount_extent(struct btrfs_trans_handle *trans,
{
struct rb_node *node;
struct file_extent_hole *hole;
+ int found = 0;
int ret = 0;
node = rb_first(&rec->holes);
while (node) {
+ found = 1;
hole = rb_entry(node, struct file_extent_hole, node);
ret = btrfs_punch_hole(trans, root, rec->ino,
hole->start, hole->len);
@@ -2677,6 +2685,13 @@ static int repair_inode_discount_extent(struct btrfs_trans_handle *trans,
rec->errors &= ~I_ERR_FILE_EXTENT_DISCOUNT;
node = rb_first(&rec->holes);
}
+ /* special case for a file losing all its file extent */
+ if (!found) {
+ ret = btrfs_punch_hole(trans, root, rec->ino, 0,
+ round_up(rec->isize, root->sectorsize));
+ if (ret < 0)
+ goto out;
+ }
printf("Fixed discount file extents for inode: %llu in root: %llu\n",
rec->ino, root->objectid);
out:
@@ -2954,7 +2969,7 @@ static struct root_backref *get_root_backref(struct root_record *rec,
}
backref = malloc(sizeof(*backref) + namelen + 1);
- memset(backref, 0, sizeof(*backref));
+ memset(backref, 0, sizeof(*backref) + namelen + 1);
backref->ref_root = ref_root;
backref->dir = dir;
backref->index = index;
@@ -3734,7 +3749,7 @@ static int maybe_free_extent_rec(struct cache_tree *extent_cache,
if (rec->content_checked && rec->owner_ref_checked &&
rec->extent_item_refs == rec->refs && rec->refs > 0 &&
rec->num_duplicates == 0 && !all_backpointers_checked(rec, 0) &&
- !rec->bad_full_backref) {
+ !rec->bad_full_backref && !rec->crossing_stripes) {
remove_cache_extent(extent_cache, &rec->cache);
free_all_extent_backrefs(rec);
list_del_init(&rec->list);
@@ -4381,6 +4396,15 @@ static int add_extent_rec(struct cache_tree *extent_cache,
if (rec->max_size < max_size)
rec->max_size = max_size;
+ /*
+ * A metadata extent can't cross stripe_len boundary, otherwise
+ * kernel scrub won't be able to handle it.
+ * 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;
maybe_free_extent_rec(extent_cache, rec);
return ret;
}
@@ -4395,6 +4419,7 @@ static int add_extent_rec(struct cache_tree *extent_cache,
rec->metadata = metadata;
rec->flag_block_full_backref = -1;
rec->bad_full_backref = 0;
+ rec->crossing_stripes = 0;
INIT_LIST_HEAD(&rec->backrefs);
INIT_LIST_HEAD(&rec->dups);
INIT_LIST_HEAD(&rec->list);
@@ -4433,6 +4458,10 @@ static int add_extent_rec(struct cache_tree *extent_cache,
rec->content_checked = 1;
rec->owner_ref_checked = 1;
}
+
+ if (metadata)
+ if (check_crossing_stripes(rec->start, rec->max_size))
+ rec->crossing_stripes = 1;
return ret;
}
@@ -7478,6 +7507,18 @@ static int check_extent_refs(struct btrfs_root *root,
err = 1;
cur_err = 1;
}
+ /*
+ * Although it's not a extent ref's problem, we reuse this
+ * routine for error reporting.
+ * No repair function yet.
+ */
+ if (rec->crossing_stripes) {
+ fprintf(stderr,
+ "bad metadata [%llu, %llu) crossing stripe boundary\n",
+ rec->start, rec->start + rec->max_size);
+ err = 1;
+ cur_err = 1;
+ }
remove_cache_extent(extent_cache, cache);
free_all_extent_backrefs(rec);
@@ -9186,8 +9227,7 @@ next:
ret = 0;
out:
free_roots_info_cache();
- if (path)
- btrfs_free_path(path);
+ btrfs_free_path(path);
if (trans)
btrfs_commit_transaction(trans, info->tree_root);
if (ret < 0)
@@ -9548,6 +9588,7 @@ out:
free_root_recs_tree(&root_cache);
close_out:
close_ctree(root);
+ btrfs_close_all_devices();
err_out:
return ret;
}
diff --git a/cmds-device.c b/cmds-device.c
index 0e60500..5f2b952 100644
--- a/cmds-device.c
+++ b/cmds-device.c
@@ -28,6 +28,7 @@
#include "ctree.h"
#include "ioctl.h"
#include "utils.h"
+#include "volumes.h"
#include "cmds-fi-usage.h"
#include "commands.h"
@@ -37,7 +38,7 @@ static const char * const device_cmd_group_usage[] = {
NULL
};
-static const char * const cmd_add_dev_usage[] = {
+static const char * const cmd_device_add_usage[] = {
"btrfs device add [options] <device> [<device>...] <path>",
"Add a device to a filesystem",
"-K|--nodiscard do not perform whole device TRIM",
@@ -45,7 +46,7 @@ static const char * const cmd_add_dev_usage[] = {
NULL
};
-static int cmd_add_dev(int argc, char **argv)
+static int cmd_device_add(int argc, char **argv)
{
char *mntpnt;
int i, fdmnt, ret=0, e;
@@ -72,22 +73,20 @@ static int cmd_add_dev(int argc, char **argv)
force = 1;
break;
default:
- usage(cmd_add_dev_usage);
+ usage(cmd_device_add_usage);
}
}
argc = argc - optind;
if (check_argc_min(argc, 2))
- usage(cmd_add_dev_usage);
+ usage(cmd_device_add_usage);
mntpnt = argv[optind + argc - 1];
- fdmnt = open_file_or_dir(mntpnt, &dirstream);
- if (fdmnt < 0) {
- fprintf(stderr, "ERROR: can't access '%s'\n", mntpnt);
+ fdmnt = btrfs_open_dir(mntpnt, &dirstream, 1);
+ if (fdmnt < 0)
return 1;
- }
for (i = optind; i < optind + argc - 1; i++){
struct btrfs_ioctl_vol_args ioctl_args;
@@ -140,10 +139,12 @@ static int cmd_add_dev(int argc, char **argv)
error_out:
close_file_or_dir(fdmnt, dirstream);
+ btrfs_close_all_devices();
return !!ret;
}
-static int _cmd_rm_dev(int argc, char **argv, const char * const *usagestr)
+static int _cmd_device_remove(int argc, char **argv,
+ const char * const *usagestr)
{
char *mntpnt;
int i, fdmnt, ret=0, e;
@@ -154,17 +155,15 @@ static int _cmd_rm_dev(int argc, char **argv, const char * const *usagestr)
mntpnt = argv[argc - 1];
- fdmnt = open_file_or_dir(mntpnt, &dirstream);
- if (fdmnt < 0) {
- fprintf(stderr, "ERROR: can't access '%s'\n", mntpnt);
+ fdmnt = btrfs_open_dir(mntpnt, &dirstream, 1);
+ if (fdmnt < 0)
return 1;
- }
for(i=1 ; i < argc - 1; i++ ){
struct btrfs_ioctl_vol_args arg;
int res;
- if (!is_block_device(argv[i])) {
+ if (is_block_device(argv[i]) != 1) {
fprintf(stderr,
"ERROR: %s is not a block device\n", argv[i]);
ret++;
@@ -192,36 +191,36 @@ static int _cmd_rm_dev(int argc, char **argv, const char * const *usagestr)
return !!ret;
}
-static const char * const cmd_rm_dev_usage[] = {
+static const char * const cmd_device_remove_usage[] = {
"btrfs device remove <device> [<device>...] <path>",
"Remove a device from a filesystem",
NULL
};
-static int cmd_rm_dev(int argc, char **argv)
+static int cmd_device_remove(int argc, char **argv)
{
- return _cmd_rm_dev(argc, argv, cmd_rm_dev_usage);
+ return _cmd_device_remove(argc, argv, cmd_device_remove_usage);
}
-static const char * const cmd_del_dev_usage[] = {
+static const char * const cmd_device_delete_usage[] = {
"btrfs device delete <device> [<device>...] <path>",
"Remove a device from a filesystem",
NULL
};
-static int cmd_del_dev(int argc, char **argv)
+static int cmd_device_delete(int argc, char **argv)
{
- return _cmd_rm_dev(argc, argv, cmd_del_dev_usage);
+ return _cmd_device_remove(argc, argv, cmd_device_delete_usage);
}
-static const char * const cmd_scan_dev_usage[] = {
+static const char * const cmd_device_scan_usage[] = {
"btrfs device scan [(-d|--all-devices)|<device> [<device>...]]",
"Scan devices for a btrfs filesystem",
" -d|--all-devices (deprecated)",
NULL
};
-static int cmd_scan_dev(int argc, char **argv)
+static int cmd_device_scan(int argc, char **argv)
{
int i;
int devstart = 1;
@@ -244,12 +243,12 @@ static int cmd_scan_dev(int argc, char **argv)
all = 1;
break;
default:
- usage(cmd_scan_dev_usage);
+ usage(cmd_device_scan_usage);
}
}
if (all && check_argc_max(argc, 2))
- usage(cmd_scan_dev_usage);
+ usage(cmd_device_scan_usage);
if (all || argc == 1) {
printf("Scanning for Btrfs filesystems\n");
@@ -265,7 +264,7 @@ static int cmd_scan_dev(int argc, char **argv)
for( i = devstart ; i < argc ; i++ ){
char *path;
- if (!is_block_device(argv[i])) {
+ if (is_block_device(argv[i]) != 1) {
fprintf(stderr,
"ERROR: %s is not a block device\n", argv[i]);
ret = 1;
@@ -289,16 +288,17 @@ static int cmd_scan_dev(int argc, char **argv)
}
out:
+ btrfs_close_all_devices();
return !!ret;
}
-static const char * const cmd_ready_dev_usage[] = {
+static const char * const cmd_device_ready_usage[] = {
"btrfs device ready <device>",
"Check device to see if it has all of its devices in cache for mounting",
NULL
};
-static int cmd_ready_dev(int argc, char **argv)
+static int cmd_device_ready(int argc, char **argv)
{
struct btrfs_ioctl_vol_args args;
int fd;
@@ -306,7 +306,7 @@ static int cmd_ready_dev(int argc, char **argv)
char *path;
if (check_argc_min(argc, 2))
- usage(cmd_ready_dev_usage);
+ usage(cmd_device_ready_usage);
fd = open("/dev/btrfs-control", O_RDWR);
if (fd < 0) {
@@ -323,7 +323,7 @@ static int cmd_ready_dev(int argc, char **argv)
goto out;
}
- if (!is_block_device(path)) {
+ if (is_block_device(path) != 1) {
fprintf(stderr,
"ERROR: %s is not a block device\n", path);
ret = 1;
@@ -346,13 +346,15 @@ out:
return ret;
}
-static const char * const cmd_dev_stats_usage[] = {
+static const char * const cmd_device_stats_usage[] = {
"btrfs device stats [-z] <path>|<device>",
- "Show current device IO stats. -z to reset stats afterwards.",
+ "Show current device IO stats.",
+ "",
+ "-z show current stats and reset values to zero",
NULL
};
-static int cmd_dev_stats(int argc, char **argv)
+static int cmd_device_stats(int argc, char **argv)
{
char *dev_path;
struct btrfs_ioctl_fs_info_args fi_args;
@@ -373,13 +375,13 @@ static int cmd_dev_stats(int argc, char **argv)
break;
case '?':
default:
- usage(cmd_dev_stats_usage);
+ usage(cmd_device_stats_usage);
}
}
argc = argc - optind;
if (check_argc_exact(argc, 1))
- usage(cmd_dev_stats_usage);
+ usage(cmd_device_stats_usage);
dev_path = argv[optind];
@@ -464,23 +466,15 @@ static int cmd_dev_stats(int argc, char **argv)
out:
free(di_args);
close_file_or_dir(fdmnt, dirstream);
+ btrfs_close_all_devices();
return err;
}
-const char * const cmd_device_usage_usage[] = {
+static const char * const cmd_device_usage_usage[] = {
"btrfs device usage [options] <path> [<path>..]",
"Show detailed information about internal allocations in devices.",
- "-b|--raw raw numbers in bytes",
- "-h|--human-readable",
- " human friendly numbers, base 1024 (default)",
- "-H human friendly numbers, base 1000",
- "--iec use 1024 as a base (KiB, MiB, GiB, TiB)",
- "--si use 1000 as a base (kB, MB, GB, TB)",
- "-k|--kbytes show sizes in KiB, or kB with --si",
- "-m|--mbytes show sizes in MiB, or MB with --si",
- "-g|--gbytes show sizes in GiB, or GB with --si",
- "-t|--tbytes show sizes in TiB, or TB with --si",
+ HELPINFO_OUTPUT_UNIT_DF,
NULL
};
@@ -513,78 +507,27 @@ out:
return ret;
}
-int cmd_device_usage(int argc, char **argv)
+static int cmd_device_usage(int argc, char **argv)
{
- unsigned unit_mode = UNITS_DEFAULT;
+ unsigned unit_mode;
int ret = 0;
- int i, more_than_one = 0;
+ int more_than_one = 0;
+ int i;
- optind = 1;
- while (1) {
- int c;
- static const struct option long_options[] = {
- { "raw", no_argument, NULL, 'b'},
- { "kbytes", no_argument, NULL, 'k'},
- { "mbytes", no_argument, NULL, 'm'},
- { "gbytes", no_argument, NULL, 'g'},
- { "tbytes", no_argument, NULL, 't'},
- { "si", no_argument, NULL, GETOPT_VAL_SI},
- { "iec", no_argument, NULL, GETOPT_VAL_IEC},
- { "human-readable", no_argument, NULL,
- GETOPT_VAL_HUMAN_READABLE},
- { NULL, 0, NULL, 0 }
- };
+ unit_mode = get_unit_mode_from_arg(&argc, argv, 1);
- c = getopt_long(argc, argv, "bhHkmgt", long_options, NULL);
- if (c < 0)
- break;
- switch (c) {
- case 'b':
- unit_mode = UNITS_RAW;
- break;
- case 'k':
- units_set_base(&unit_mode, UNITS_KBYTES);
- break;
- case 'm':
- units_set_base(&unit_mode, UNITS_MBYTES);
- break;
- case 'g':
- units_set_base(&unit_mode, UNITS_GBYTES);
- break;
- case 't':
- units_set_base(&unit_mode, UNITS_TBYTES);
- break;
- case GETOPT_VAL_HUMAN_READABLE:
- case 'h':
- unit_mode = UNITS_HUMAN_BINARY;
- break;
- case 'H':
- unit_mode = UNITS_HUMAN_DECIMAL;
- break;
- case GETOPT_VAL_SI:
- units_set_mode(&unit_mode, UNITS_DECIMAL);
- break;
- case GETOPT_VAL_IEC:
- units_set_mode(&unit_mode, UNITS_BINARY);
- break;
- default:
- usage(cmd_device_usage_usage);
- }
- }
-
- if (check_argc_min(argc - optind, 1))
+ if (check_argc_min(argc, 2) || argv[1][0] == '-')
usage(cmd_device_usage_usage);
- for (i = optind; i < argc ; i++) {
+ for (i = 1; i < argc; i++) {
int fd;
- DIR *dirstream = NULL;
+ DIR *dirstream = NULL;
+
if (more_than_one)
printf("\n");
- fd = open_file_or_dir(argv[i], &dirstream);
+ fd = btrfs_open_dir(argv[i], &dirstream, 1);
if (fd < 0) {
- fprintf(stderr, "ERROR: can't access '%s'\n",
- argv[1]);
ret = 1;
goto out;
}
@@ -605,12 +548,13 @@ static const char device_cmd_group_info[] =
const struct cmd_group device_cmd_group = {
device_cmd_group_usage, device_cmd_group_info, {
- { "add", cmd_add_dev, cmd_add_dev_usage, NULL, 0 },
- { "delete", cmd_del_dev, cmd_del_dev_usage, NULL, CMD_ALIAS },
- { "remove", cmd_rm_dev, cmd_rm_dev_usage, NULL, 0 },
- { "scan", cmd_scan_dev, cmd_scan_dev_usage, NULL, 0 },
- { "ready", cmd_ready_dev, cmd_ready_dev_usage, NULL, 0 },
- { "stats", cmd_dev_stats, cmd_dev_stats_usage, NULL, 0 },
+ { "add", cmd_device_add, cmd_device_add_usage, NULL, 0 },
+ { "delete", cmd_device_delete, cmd_device_delete_usage, NULL,
+ CMD_ALIAS },
+ { "remove", cmd_device_remove, cmd_device_remove_usage, NULL, 0 },
+ { "scan", cmd_device_scan, cmd_device_scan_usage, NULL, 0 },
+ { "ready", cmd_device_ready, cmd_device_ready_usage, NULL, 0 },
+ { "stats", cmd_device_stats, cmd_device_stats_usage, NULL, 0 },
{ "usage", cmd_device_usage,
cmd_device_usage_usage, NULL, 0 },
NULL_CMD_STRUCT
diff --git a/cmds-fi-usage.c b/cmds-fi-usage.c
index adf1c27..50d6333 100644
--- a/cmds-fi-usage.c
+++ b/cmds-fi-usage.c
@@ -504,7 +504,7 @@ static int cmp_device_info(const void *a, const void *b)
static int load_device_info(int fd, struct device_info **device_info_ptr,
int *device_info_count)
{
- int ret, i, ndevs, e;
+ int ret, i, ndevs;
struct btrfs_ioctl_fs_info_args fi_args;
struct btrfs_ioctl_dev_info_args dev_info;
struct device_info *info;
@@ -513,12 +513,11 @@ static int load_device_info(int fd, struct device_info **device_info_ptr,
*device_info_ptr = 0;
ret = ioctl(fd, BTRFS_IOC_FS_INFO, &fi_args);
- e = errno;
- if (e == EPERM)
- return -e;
if (ret < 0) {
+ if (errno == EPERM)
+ return -errno;
fprintf(stderr, "ERROR: cannot get filesystem info - %s\n",
- strerror(e));
+ strerror(errno));
return 1;
}
@@ -859,76 +858,30 @@ out:
const char * const cmd_filesystem_usage_usage[] = {
"btrfs filesystem usage [options] <path> [<path>..]",
"Show detailed information about internal filesystem usage .",
- "-b|--raw raw numbers in bytes",
- "-h|--human-readable",
- " human friendly numbers, base 1024 (default)",
- "-H human friendly numbers, base 1000",
- "--iec use 1024 as a base (KiB, MiB, GiB, TiB)",
- "--si use 1000 as a base (kB, MB, GB, TB)",
- "-k|--kbytes show sizes in KiB, or kB with --si",
- "-m|--mbytes show sizes in MiB, or MB with --si",
- "-g|--gbytes show sizes in GiB, or GB with --si",
- "-t|--tbytes show sizes in TiB, or TB with --si",
+ HELPINFO_OUTPUT_UNIT_DF,
"-T show data in tabular format",
NULL
};
int cmd_filesystem_usage(int argc, char **argv)
{
- unsigned unit_mode = UNITS_DEFAULT;
int ret = 0;
- int i, more_than_one = 0;
- int tabular = 0;
+ unsigned unit_mode;
+ int i;
+ int more_than_one = 0;
+ int tabular = 0;
+
+ unit_mode = get_unit_mode_from_arg(&argc, argv, 1);
optind = 1;
while (1) {
int c;
- static const struct option long_options[] = {
- { "raw", no_argument, NULL, 'b'},
- { "kbytes", no_argument, NULL, 'k'},
- { "mbytes", no_argument, NULL, 'm'},
- { "gbytes", no_argument, NULL, 'g'},
- { "tbytes", no_argument, NULL, 't'},
- { "si", no_argument, NULL, GETOPT_VAL_SI},
- { "iec", no_argument, NULL, GETOPT_VAL_IEC},
- { "human-readable", no_argument, NULL,
- GETOPT_VAL_HUMAN_READABLE},
- { NULL, 0, NULL, 0 }
- };
-
- c = getopt_long(argc, argv, "bhHkmgtT", long_options, NULL);
+ c = getopt(argc, argv, "T");
if (c < 0)
break;
+
switch (c) {
- case 'b':
- unit_mode = UNITS_RAW;
- break;
- case 'k':
- units_set_base(&unit_mode, UNITS_KBYTES);
- break;
- case 'm':
- units_set_base(&unit_mode, UNITS_MBYTES);
- break;
- case 'g':
- units_set_base(&unit_mode, UNITS_GBYTES);
- break;
- case 't':
- units_set_base(&unit_mode, UNITS_TBYTES);
- break;
- case GETOPT_VAL_HUMAN_READABLE:
- case 'h':
- unit_mode = UNITS_HUMAN_BINARY;
- break;
- case 'H':
- unit_mode = UNITS_HUMAN_DECIMAL;
- break;
- case GETOPT_VAL_SI:
- units_set_mode(&unit_mode, UNITS_DECIMAL);
- break;
- case GETOPT_VAL_IEC:
- units_set_mode(&unit_mode, UNITS_BINARY);
- break;
case 'T':
tabular = 1;
break;
diff --git a/cmds-filesystem.c b/cmds-filesystem.c
index 800aa4d..02def40 100644
--- a/cmds-filesystem.c
+++ b/cmds-filesystem.c
@@ -121,19 +121,10 @@ static const char * const filesystem_cmd_group_usage[] = {
};
static const char * const cmd_filesystem_df_usage[] = {
- "btrfs filesystem df [options] <path>",
- "Show space usage information for a mount point",
- "-b|--raw raw numbers in bytes",
- "-h|--human-readable",
- " human friendly numbers, base 1024 (default)",
- "-H human friendly numbers, base 1000",
- "--iec use 1024 as a base (KiB, MiB, GiB, TiB)",
- "--si use 1000 as a base (kB, MB, GB, TB)",
- "-k|--kbytes show sizes in KiB, or kB with --si",
- "-m|--mbytes show sizes in MiB, or MB with --si",
- "-g|--gbytes show sizes in GiB, or GB with --si",
- "-t|--tbytes show sizes in TiB, or TB with --si",
- NULL
+ "btrfs filesystem df [options] <path>",
+ "Show space usage information for a mount point",
+ HELPINFO_OUTPUT_UNIT_DF,
+ NULL
};
static int get_df(int fd, struct btrfs_ioctl_space_args **sargs_ret)
@@ -205,64 +196,14 @@ static int cmd_filesystem_df(int argc, char **argv)
int fd;
char *path;
DIR *dirstream = NULL;
- unsigned unit_mode = UNITS_DEFAULT;
+ unsigned unit_mode;
- while (1) {
- int c;
- static const struct option long_options[] = {
- { "raw", no_argument, NULL, 'b'},
- { "kbytes", no_argument, NULL, 'k'},
- { "mbytes", no_argument, NULL, 'm'},
- { "gbytes", no_argument, NULL, 'g'},
- { "tbytes", no_argument, NULL, 't'},
- { "si", no_argument, NULL, GETOPT_VAL_SI},
- { "iec", no_argument, NULL, GETOPT_VAL_IEC},
- { "human-readable", no_argument, NULL,
- GETOPT_VAL_HUMAN_READABLE},
- { NULL, 0, NULL, 0 }
- };
+ unit_mode = get_unit_mode_from_arg(&argc, argv, 1);
- c = getopt_long(argc, argv, "bhHkmgt", long_options, NULL);
- if (c < 0)
- break;
- switch (c) {
- case 'b':
- unit_mode = UNITS_RAW;
- break;
- case 'k':
- units_set_base(&unit_mode, UNITS_KBYTES);
- break;
- case 'm':
- units_set_base(&unit_mode, UNITS_MBYTES);
- break;
- case 'g':
- units_set_base(&unit_mode, UNITS_GBYTES);
- break;
- case 't':
- units_set_base(&unit_mode, UNITS_TBYTES);
- break;
- case GETOPT_VAL_HUMAN_READABLE:
- case 'h':
- unit_mode = UNITS_HUMAN_BINARY;
- break;
- case 'H':
- unit_mode = UNITS_HUMAN_DECIMAL;
- break;
- case GETOPT_VAL_SI:
- units_set_mode(&unit_mode, UNITS_DECIMAL);
- break;
- case GETOPT_VAL_IEC:
- units_set_mode(&unit_mode, UNITS_BINARY);
- break;
- default:
- usage(cmd_filesystem_df_usage);
- }
- }
-
- if (check_argc_exact(argc, optind + 1))
+ if (argc != 2 || argv[1][0] == '-')
usage(cmd_filesystem_df_usage);
- path = argv[optind];
+ path = argv[1];
fd = open_file_or_dir(path, &dirstream);
if (fd < 0) {
@@ -527,13 +468,20 @@ static int btrfs_scan_kernel(void *search, unsigned unit_mode)
goto out;
}
- if (get_label_mounted(mnt->mnt_dir, label)) {
+ ret = get_label_mounted(mnt->mnt_dir, label);
+ /* provide backward kernel compatibility */
+ if (ret == -ENOTTY)
+ ret = get_label_unmounted(
+ (const char *)dev_info_arg->path, label);
+
+ if (ret) {
kfree(dev_info_arg);
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;
}
@@ -549,6 +497,7 @@ static int btrfs_scan_kernel(void *search, unsigned unit_mode)
if (fd != -1)
close(fd);
kfree(dev_info_arg);
+ dev_info_arg = NULL;
}
out:
@@ -815,24 +764,17 @@ fail_out:
goto out;
}
-static const char * const cmd_show_usage[] = {
+static const char * const cmd_filesystem_show_usage[] = {
"btrfs filesystem show [options] [<path>|<uuid>|<device>|label]",
"Show the structure of a filesystem",
"-d|--all-devices show only disks under /dev containing btrfs filesystem",
"-m|--mounted show only mounted btrfs",
- "--raw raw numbers in bytes",
- "--human-readable human friendly numbers, base 1024 (default)",
- "--iec use 1024 as a base (KiB, MiB, GiB, TiB)",
- "--si use 1000 as a base (kB, MB, GB, TB)",
- "--kbytes show sizes in KiB, or kB with --si",
- "--mbytes show sizes in MiB, or MB with --si",
- "--gbytes show sizes in GiB, or GB with --si",
- "--tbytes show sizes in TiB, or TB with --si",
+ HELPINFO_OUTPUT_UNIT,
"If no argument is given, structure of all present filesystems is shown.",
NULL
};
-static int cmd_show(int argc, char **argv)
+static int cmd_filesystem_show(int argc, char **argv)
{
LIST_HEAD(all_uuids);
struct btrfs_fs_devices *fs_devices;
@@ -845,23 +787,16 @@ static int cmd_show(int argc, char **argv)
char path[PATH_MAX];
__u8 fsid[BTRFS_FSID_SIZE];
char uuid_buf[BTRFS_UUID_UNPARSED_SIZE];
- unsigned unit_mode = UNITS_DEFAULT;
+ unsigned unit_mode;
int found = 0;
+ unit_mode = get_unit_mode_from_arg(&argc, argv, 0);
+
while (1) {
int c;
static const struct option long_options[] = {
{ "all-devices", no_argument, NULL, 'd'},
{ "mounted", no_argument, NULL, 'm'},
- { "raw", no_argument, NULL, GETOPT_VAL_RAW},
- { "kbytes", no_argument, NULL, GETOPT_VAL_KBYTES},
- { "mbytes", no_argument, NULL, GETOPT_VAL_MBYTES},
- { "gbytes", no_argument, NULL, GETOPT_VAL_GBYTES},
- { "tbytes", no_argument, NULL, GETOPT_VAL_TBYTES},
- { "si", no_argument, NULL, GETOPT_VAL_SI},
- { "iec", no_argument, NULL, GETOPT_VAL_IEC},
- { "human-readable", no_argument, NULL,
- GETOPT_VAL_HUMAN_READABLE},
{ NULL, 0, NULL, 0 }
};
@@ -875,42 +810,18 @@ static int cmd_show(int argc, char **argv)
case 'm':
where = BTRFS_SCAN_MOUNTED;
break;
- case GETOPT_VAL_RAW:
- units_set_mode(&unit_mode, UNITS_RAW);
- break;
- case GETOPT_VAL_KBYTES:
- units_set_base(&unit_mode, UNITS_KBYTES);
- break;
- case GETOPT_VAL_MBYTES:
- units_set_base(&unit_mode, UNITS_MBYTES);
- break;
- case GETOPT_VAL_GBYTES:
- units_set_base(&unit_mode, UNITS_GBYTES);
- break;
- case GETOPT_VAL_TBYTES:
- units_set_base(&unit_mode, UNITS_TBYTES);
- break;
- case GETOPT_VAL_SI:
- units_set_mode(&unit_mode, UNITS_DECIMAL);
- break;
- case GETOPT_VAL_IEC:
- units_set_mode(&unit_mode, UNITS_BINARY);
- break;
- case GETOPT_VAL_HUMAN_READABLE:
- units_set_mode(&unit_mode, UNITS_HUMAN_BINARY);
- break;
default:
- usage(cmd_show_usage);
+ usage(cmd_filesystem_show_usage);
}
}
if (check_argc_max(argc, optind + 1))
- usage(cmd_show_usage);
+ usage(cmd_filesystem_show_usage);
if (argc > optind) {
search = argv[optind];
if (strlen(search) == 0)
- usage(cmd_show_usage);
+ usage(cmd_filesystem_show_usage);
type = check_arg_type(search);
/*
@@ -1007,20 +918,20 @@ out:
return ret;
}
-static const char * const cmd_sync_usage[] = {
+static const char * const cmd_filesystem_sync_usage[] = {
"btrfs filesystem sync <path>",
"Force a sync on a filesystem",
NULL
};
-static int cmd_sync(int argc, char **argv)
+static int cmd_filesystem_sync(int argc, char **argv)
{
int fd, res, e;
char *path;
DIR *dirstream = NULL;
if (check_argc_exact(argc, 2))
- usage(cmd_sync_usage);
+ usage(cmd_filesystem_sync_usage);
path = argv[1];
@@ -1055,7 +966,7 @@ static int parse_compress_type(char *s)
};
}
-static const char * const cmd_defrag_usage[] = {
+static const char * const cmd_filesystem_defrag_usage[] = {
"btrfs filesystem defragment [options] <file>|<dir> [<file>|<dir>...]",
"Defragment a file or a directory",
"",
@@ -1121,7 +1032,7 @@ error:
return 0;
}
-static int cmd_defrag(int argc, char **argv)
+static int cmd_filesystem_defrag(int argc, char **argv)
{
int fd;
int flush = 0;
@@ -1131,7 +1042,6 @@ static int cmd_defrag(int argc, char **argv)
int i;
int recursive = 0;
int ret = 0;
- struct btrfs_ioctl_defrag_range_args range;
int e = 0;
int compress_type = BTRFS_COMPRESS_NONE;
DIR *dirstream;
@@ -1172,8 +1082,9 @@ static int cmd_defrag(int argc, char **argv)
thresh = parse_size(optarg);
if (thresh > (u32)-1) {
fprintf(stderr,
- "WARNING: target extent size %llu too big, trimmed to %u",
+ "WARNING: target extent size %llu too big, trimmed to %u\n",
thresh, (u32)-1);
+ thresh = (u32)-1;
}
defrag_global_fancy_ioctl = 1;
break;
@@ -1181,14 +1092,14 @@ static int cmd_defrag(int argc, char **argv)
recursive = 1;
break;
default:
- usage(cmd_defrag_usage);
+ usage(cmd_filesystem_defrag_usage);
}
}
if (check_argc_min(argc - optind, 1))
- usage(cmd_defrag_usage);
+ usage(cmd_filesystem_defrag_usage);
- memset(&defrag_global_range, 0, sizeof(range));
+ memset(&defrag_global_range, 0, sizeof(defrag_global_range));
defrag_global_range.start = start;
defrag_global_range.len = len;
defrag_global_range.extent_thresh = (u32)thresh;
@@ -1270,7 +1181,7 @@ static int cmd_defrag(int argc, char **argv)
return !!defrag_global_errors;
}
-static const char * const cmd_resize_usage[] = {
+static const char * const cmd_filesystem_resize_usage[] = {
"btrfs filesystem resize [devid:][+/-]<newsize>[kKmMgGtTpPeE]|[devid:]max <path>",
"Resize a filesystem",
"If 'max' is passed, the filesystem will occupy all available space",
@@ -1279,7 +1190,7 @@ static const char * const cmd_resize_usage[] = {
NULL
};
-static int cmd_resize(int argc, char **argv)
+static int cmd_filesystem_resize(int argc, char **argv)
{
struct btrfs_ioctl_vol_args args;
int fd, res, len, e;
@@ -1288,7 +1199,7 @@ static int cmd_resize(int argc, char **argv)
struct stat st;
if (check_argc_exact(argc, 3))
- usage(cmd_resize_usage);
+ usage(cmd_filesystem_resize_usage);
amount = argv[1];
path = argv[2];
@@ -1327,8 +1238,16 @@ static int cmd_resize(int argc, char **argv)
e = errno;
close_file_or_dir(fd, dirstream);
if( res < 0 ){
- fprintf(stderr, "ERROR: unable to resize '%s' - %s\n",
- path, strerror(e));
+ switch (e) {
+ case EFBIG:
+ fprintf(stderr, "ERROR: unable to resize '%s' - no enouth free space\n",
+ path);
+ break;
+ default:
+ fprintf(stderr, "ERROR: unable to resize '%s' - %s\n",
+ path, strerror(e));
+ break;
+ }
return 1;
} else if (res > 0) {
const char *err_str = btrfs_err_str(res);
@@ -1346,7 +1265,7 @@ static int cmd_resize(int argc, char **argv)
return 0;
}
-static const char * const cmd_label_usage[] = {
+static const char * const cmd_filesystem_label_usage[] = {
"btrfs filesystem label [<device>|<mount_point>] [<newlabel>]",
"Get or change the label of a filesystem",
"With one argument, get the label of filesystem on <device>.",
@@ -1354,10 +1273,10 @@ static const char * const cmd_label_usage[] = {
NULL
};
-static int cmd_label(int argc, char **argv)
+static int cmd_filesystem_label(int argc, char **argv)
{
if (check_argc_min(argc, 2) || check_argc_max(argc, 3))
- usage(cmd_label_usage);
+ usage(cmd_filesystem_label_usage);
if (argc > 2) {
return set_label(argv[1], argv[2]);
@@ -1379,12 +1298,18 @@ 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 },
- { "show", cmd_show, cmd_show_usage, NULL, 0 },
- { "sync", cmd_sync, cmd_sync_usage, NULL, 0 },
- { "defragment", cmd_defrag, cmd_defrag_usage, NULL, 0 },
- { "balance", cmd_balance, NULL, &balance_cmd_group, CMD_HIDDEN },
- { "resize", cmd_resize, cmd_resize_usage, NULL, 0 },
- { "label", cmd_label, cmd_label_usage, NULL, 0 },
+ { "show", cmd_filesystem_show, cmd_filesystem_show_usage, NULL,
+ 0 },
+ { "sync", cmd_filesystem_sync, cmd_filesystem_sync_usage, NULL,
+ 0 },
+ { "defragment", cmd_filesystem_defrag,
+ cmd_filesystem_defrag_usage, NULL, 0 },
+ { "balance", cmd_balance, NULL, &balance_cmd_group,
+ CMD_HIDDEN },
+ { "resize", cmd_filesystem_resize, cmd_filesystem_resize_usage,
+ NULL, 0 },
+ { "label", cmd_filesystem_label, cmd_filesystem_label_usage,
+ NULL, 0 },
{ "usage", cmd_filesystem_usage,
cmd_filesystem_usage_usage, NULL, 0 },
diff --git a/cmds-inspect.c b/cmds-inspect.c
index 71451fe..fc3db99 100644
--- a/cmds-inspect.c
+++ b/cmds-inspect.c
@@ -20,13 +20,15 @@
#include <stdint.h>
#include <sys/ioctl.h>
#include <errno.h>
+#include <getopt.h>
+#include <limits.h>
#include "kerncompat.h"
#include "ioctl.h"
#include "utils.h"
#include "ctree.h"
#include "send-utils.h"
-
+#include "disk-io.h"
#include "commands.h"
#include "btrfs-list.h"
@@ -81,7 +83,7 @@ out:
return !!ret;
}
-static const char * const cmd_inode_resolve_usage[] = {
+static const char * const cmd_inspect_inode_resolve_usage[] = {
"btrfs inspect-internal inode-resolve [-v] <inode> <path>",
"Get file system paths for the given inode",
"",
@@ -89,7 +91,7 @@ static const char * const cmd_inode_resolve_usage[] = {
NULL
};
-static int cmd_inode_resolve(int argc, char **argv)
+static int cmd_inspect_inode_resolve(int argc, char **argv)
{
int fd;
int verbose = 0;
@@ -107,12 +109,12 @@ static int cmd_inode_resolve(int argc, char **argv)
verbose = 1;
break;
default:
- usage(cmd_inode_resolve_usage);
+ usage(cmd_inspect_inode_resolve_usage);
}
}
if (check_argc_exact(argc - optind, 2))
- usage(cmd_inode_resolve_usage);
+ usage(cmd_inspect_inode_resolve_usage);
fd = open_file_or_dir(argv[optind+1], &dirstream);
if (fd < 0) {
@@ -127,7 +129,7 @@ static int cmd_inode_resolve(int argc, char **argv)
}
-static const char * const cmd_logical_resolve_usage[] = {
+static const char * const cmd_inspect_logical_resolve_usage[] = {
"btrfs inspect-internal logical-resolve [-Pv] [-s bufsize] <logical> <path>",
"Get file system paths for the given logical address",
"-P skip the path resolving and print the inodes instead",
@@ -138,7 +140,7 @@ static const char * const cmd_logical_resolve_usage[] = {
NULL
};
-static int cmd_logical_resolve(int argc, char **argv)
+static int cmd_inspect_logical_resolve(int argc, char **argv)
{
int ret;
int fd;
@@ -170,12 +172,12 @@ static int cmd_logical_resolve(int argc, char **argv)
size = arg_strtou64(optarg);
break;
default:
- usage(cmd_logical_resolve_usage);
+ usage(cmd_inspect_logical_resolve_usage);
}
}
if (check_argc_exact(argc - optind, 2))
- usage(cmd_logical_resolve_usage);
+ usage(cmd_inspect_logical_resolve_usage);
size = min(size, (u64)64 * 1024);
inodes = malloc(size);
@@ -259,13 +261,13 @@ out:
return !!ret;
}
-static const char * const cmd_subvolid_resolve_usage[] = {
+static const char * const cmd_inspect_subvolid_resolve_usage[] = {
"btrfs inspect-internal subvolid-resolve <subvolid> <path>",
"Get file system paths for the given subvolume ID.",
NULL
};
-static int cmd_subvolid_resolve(int argc, char **argv)
+static int cmd_inspect_subvolid_resolve(int argc, char **argv)
{
int ret;
int fd = -1;
@@ -274,7 +276,7 @@ static int cmd_subvolid_resolve(int argc, char **argv)
DIR *dirstream = NULL;
if (check_argc_exact(argc, 3))
- usage(cmd_subvolid_resolve_usage);
+ usage(cmd_inspect_subvolid_resolve_usage);
fd = open_file_or_dir(argv[2], &dirstream);
if (fd < 0) {
@@ -301,13 +303,13 @@ out:
return ret ? 1 : 0;
}
-static const char* const cmd_rootid_usage[] = {
+static const char* const cmd_inspect_rootid_usage[] = {
"btrfs inspect-internal rootid <path>",
"Get tree ID of the containing subvolume of path.",
NULL
};
-static int cmd_rootid(int argc, char **argv)
+static int cmd_inspect_rootid(int argc, char **argv)
{
int ret;
int fd = -1;
@@ -315,7 +317,7 @@ static int cmd_rootid(int argc, char **argv)
DIR *dirstream = NULL;
if (check_argc_exact(argc, 2))
- usage(cmd_rootid_usage);
+ usage(cmd_inspect_rootid_usage);
fd = open_file_or_dir(argv[1], &dirstream);
if (fd < 0) {
@@ -338,18 +340,313 @@ out:
return !!ret;
}
+struct dev_extent_elem {
+ u64 start;
+ /* inclusive end */
+ u64 end;
+ struct list_head list;
+};
+
+static int add_dev_extent(struct list_head *list,
+ const u64 start, const u64 end,
+ const int append)
+{
+ struct dev_extent_elem *e;
+
+ e = malloc(sizeof(*e));
+ if (!e)
+ return -ENOMEM;
+
+ e->start = start;
+ e->end = end;
+
+ if (append)
+ list_add_tail(&e->list, list);
+ else
+ list_add(&e->list, list);
+
+ return 0;
+}
+
+static void free_dev_extent_list(struct list_head *list)
+{
+ while (!list_empty(list)) {
+ struct dev_extent_elem *e;
+
+ e = list_first_entry(list, struct dev_extent_elem, list);
+ list_del(&e->list);
+ free(e);
+ }
+}
+
+static int hole_includes_sb_mirror(const u64 start, const u64 end)
+{
+ int i;
+ int ret = 0;
+
+ for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) {
+ u64 bytenr = btrfs_sb_offset(i);
+
+ if (bytenr >= start && bytenr <= end) {
+ ret = 1;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static void adjust_dev_min_size(struct list_head *extents,
+ struct list_head *holes,
+ u64 *min_size)
+{
+ /*
+ * If relocation of the block group of a device extent must happen (see
+ * below) scratch space is used for the relocation. So track here the
+ * size of the largest device extent that has to be relocated. We track
+ * only the largest and not the sum of the sizes of all relocated block
+ * groups because after each block group is relocated the running
+ * transaction is committed so that pinned space is released.
+ */
+ u64 scratch_space = 0;
+
+ /*
+ * List of device extents is sorted by descending order of the extent's
+ * end offset. If some extent goes beyond the computed minimum size,
+ * which initially matches the sum of the lenghts of all extents,
+ * we need to check if the extent can be relocated to an hole in the
+ * device between [0, *min_size[ (which is what the resize ioctl does).
+ */
+ while (!list_empty(extents)) {
+ struct dev_extent_elem *e;
+ struct dev_extent_elem *h;
+ int found = 0;
+ u64 extent_len;
+ u64 hole_len = 0;
+
+ e = list_first_entry(extents, struct dev_extent_elem, list);
+ if (e->end <= *min_size)
+ break;
+
+ /*
+ * Our extent goes beyond the computed *min_size. See if we can
+ * find a hole large enough to relocate it to. If not we must stop
+ * and set *min_size to the end of the extent.
+ */
+ extent_len = e->end - e->start + 1;
+ list_for_each_entry(h, holes, list) {
+ hole_len = h->end - h->start + 1;
+ if (hole_len >= extent_len) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ *min_size = e->end + 1;
+ break;
+ }
+
+ /*
+ * If the hole found contains the location for a superblock
+ * mirror, we are pessimistic and require allocating one
+ * more extent of the same size. This is because the block
+ * group could be in the worst case used by a single extent
+ * with a size >= (block_group.length - superblock.size).
+ */
+ if (hole_includes_sb_mirror(h->start,
+ h->start + extent_len - 1))
+ *min_size += extent_len;
+
+ if (hole_len > extent_len) {
+ h->start += extent_len;
+ } else {
+ list_del(&h->list);
+ free(h);
+ }
+
+ list_del(&e->list);
+ free(e);
+
+ if (extent_len > scratch_space)
+ scratch_space = extent_len;
+ }
+
+ if (scratch_space) {
+ *min_size += scratch_space;
+ /*
+ * Chunk allocation requires inserting/updating items in the
+ * chunk tree, so often this can lead to the need of allocating
+ * a new system chunk too, which has a maximum size of 32Mb.
+ */
+ *min_size += 32 * 1024 * 1024;
+ }
+}
+
+static int print_min_dev_size(int fd, u64 devid)
+{
+ int ret = 1;
+ /*
+ * Device allocations starts at 1Mb or at the value passed through the
+ * mount option alloc_start if it's bigger than 1Mb. The alloc_start
+ * option is used for debugging and testing only, and recently the
+ * possibility of deprecating/removing it has been discussed, so we
+ * ignore it here.
+ */
+ u64 min_size = 1 * 1024 * 1024ull;
+ struct btrfs_ioctl_search_args args;
+ struct btrfs_ioctl_search_key *sk = &args.key;
+ u64 last_pos = (u64)-1;
+ LIST_HEAD(extents);
+ LIST_HEAD(holes);
+
+ memset(&args, 0, sizeof(args));
+ sk->tree_id = BTRFS_DEV_TREE_OBJECTID;
+ sk->min_objectid = devid;
+ sk->max_objectid = devid;
+ sk->max_type = BTRFS_DEV_EXTENT_KEY;
+ sk->min_type = BTRFS_DEV_EXTENT_KEY;
+ sk->min_offset = 0;
+ sk->max_offset = (u64)-1;
+ sk->min_transid = 0;
+ sk->max_transid = (u64)-1;
+ sk->nr_items = 4096;
+
+ while (1) {
+ int i;
+ struct btrfs_ioctl_search_header *sh;
+ unsigned long off = 0;
+
+ ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
+ if (ret < 0) {
+ fprintf(stderr,
+ "Error invoking tree search ioctl: %s\n",
+ strerror(errno));
+ ret = 1;
+ goto out;
+ }
+
+ if (sk->nr_items == 0)
+ break;
+
+ for (i = 0; i < sk->nr_items; i++) {
+ struct btrfs_dev_extent *extent;
+ u64 len;
+
+ sh = (struct btrfs_ioctl_search_header *)(args.buf +
+ off);
+ off += sizeof(*sh);
+ extent = (struct btrfs_dev_extent *)(args.buf + off);
+ off += sh->len;
+
+ sk->min_objectid = sh->objectid;
+ sk->min_type = sh->type;
+ sk->min_offset = sh->offset + 1;
+
+ if (sh->objectid != devid ||
+ sh->type != BTRFS_DEV_EXTENT_KEY)
+ continue;
+
+ len = btrfs_stack_dev_extent_length(extent);
+ min_size += len;
+ ret = add_dev_extent(&extents, sh->offset,
+ sh->offset + len - 1, 0);
+
+ if (!ret && last_pos != (u64)-1 &&
+ last_pos != sh->offset)
+ ret = add_dev_extent(&holes, last_pos,
+ sh->offset - 1, 1);
+ if (ret) {
+ fprintf(stderr, "Error: %s\n", strerror(-ret));
+ ret = 1;
+ goto out;
+ }
+
+ last_pos = sh->offset + len;
+ }
+
+ if (sk->min_type != BTRFS_DEV_EXTENT_KEY ||
+ sk->min_objectid != devid)
+ break;
+ }
+
+ adjust_dev_min_size(&extents, &holes, &min_size);
+ printf("%llu bytes (%s)\n", min_size, pretty_size(min_size));
+ ret = 0;
+out:
+ free_dev_extent_list(&extents);
+ free_dev_extent_list(&holes);
+
+ return ret;
+}
+
+static const char* const cmd_inspect_min_dev_size_usage[] = {
+ "btrfs inspect-internal min-dev-size [options] <path>",
+ "Get the minimum size the device can be shrunk to. The",
+ "device id 1 is used by default.",
+ "--id DEVID specify the device id to query",
+ NULL
+};
+
+static int cmd_inspect_min_dev_size(int argc, char **argv)
+{
+ int ret;
+ int fd = -1;
+ DIR *dirstream = NULL;
+ u64 devid = 1;
+
+ while (1) {
+ int c;
+ enum { GETOPT_VAL_DEVID = 256 };
+ static const struct option long_options[] = {
+ { "id", required_argument, NULL, GETOPT_VAL_DEVID },
+ {NULL, 0, NULL, 0}
+ };
+
+ c = getopt_long(argc, argv, "", long_options, NULL);
+ if (c < 0)
+ break;
+
+ switch (c) {
+ case GETOPT_VAL_DEVID:
+ devid = arg_strtou64(optarg);
+ break;
+ default:
+ usage(cmd_inspect_min_dev_size_usage);
+ }
+ }
+ if (check_argc_exact(argc - optind, 1))
+ usage(cmd_inspect_min_dev_size_usage);
+
+ fd = open_file_or_dir(argv[optind], &dirstream);
+ if (fd < 0) {
+ fprintf(stderr, "ERROR: can't access '%s'\n", argv[optind]);
+ ret = -ENOENT;
+ goto out;
+ }
+
+ ret = print_min_dev_size(fd, devid);
+out:
+ close_file_or_dir(fd, dirstream);
+
+ return !!ret;
+}
+
static const char inspect_cmd_group_info[] =
"query various internal information";
const struct cmd_group inspect_cmd_group = {
inspect_cmd_group_usage, inspect_cmd_group_info, {
- { "inode-resolve", cmd_inode_resolve, cmd_inode_resolve_usage,
- NULL, 0 },
- { "logical-resolve", cmd_logical_resolve,
- cmd_logical_resolve_usage, NULL, 0 },
- { "subvolid-resolve", cmd_subvolid_resolve,
- cmd_subvolid_resolve_usage, NULL, 0 },
- { "rootid", cmd_rootid, cmd_rootid_usage, NULL, 0 },
+ { "inode-resolve", cmd_inspect_inode_resolve,
+ cmd_inspect_inode_resolve_usage, NULL, 0 },
+ { "logical-resolve", cmd_inspect_logical_resolve,
+ cmd_inspect_logical_resolve_usage, NULL, 0 },
+ { "subvolid-resolve", cmd_inspect_subvolid_resolve,
+ cmd_inspect_subvolid_resolve_usage, NULL, 0 },
+ { "rootid", cmd_inspect_rootid, cmd_inspect_rootid_usage, NULL,
+ 0 },
+ { "min-dev-size", cmd_inspect_min_dev_size,
+ cmd_inspect_min_dev_size_usage, NULL, 0 },
NULL_CMD_STRUCT
}
};
diff --git a/cmds-property.c b/cmds-property.c
index 5f1dd5b..0ffd250 100644
--- a/cmds-property.c
+++ b/cmds-property.c
@@ -32,7 +32,7 @@ static const char * const property_cmd_group_usage[] = {
NULL
};
-static const char * const cmd_get_usage[] = {
+static const char * const cmd_property_get_usage[] = {
"btrfs property get [-t <type>] <object> [<name>]",
"Gets a property from a btrfs object.",
"If no name is specified, all properties for the given object are",
@@ -45,7 +45,7 @@ static const char * const cmd_get_usage[] = {
NULL
};
-static const char * const cmd_set_usage[] = {
+static const char * const cmd_property_set_usage[] = {
"btrfs property set [-t <type>] <object> <name> <value>",
"Sets a property on a btrfs object.",
"Please see the help of 'btrfs property get' for a description of",
@@ -53,7 +53,7 @@ static const char * const cmd_set_usage[] = {
NULL
};
-static const char * const cmd_list_usage[] = {
+static const char * const cmd_property_list_usage[] = {
"btrfs property list [-t <type>] <object>",
"Lists available properties with their descriptions for the given object.",
"Please see the help of 'btrfs property get' for a description of",
@@ -394,7 +394,7 @@ static void parse_args(int argc, char **argv,
}
}
-static int cmd_get(int argc, char **argv)
+static int cmd_property_get(int argc, char **argv)
{
int ret;
char *object = NULL;
@@ -402,12 +402,13 @@ static int cmd_get(int argc, char **argv)
int types = 0;
if (check_argc_min(argc, 2) || check_argc_max(argc, 5))
- usage(cmd_get_usage);
+ usage(cmd_property_get_usage);
- parse_args(argc, argv, cmd_get_usage, &types, &object, &name, NULL);
+ parse_args(argc, argv, cmd_property_get_usage, &types, &object, &name,
+ NULL);
if (!object) {
fprintf(stderr, "ERROR: invalid arguments.\n");
- usage(cmd_set_usage);
+ usage(cmd_property_set_usage);
}
if (name)
@@ -418,7 +419,7 @@ static int cmd_get(int argc, char **argv)
return ret;
}
-static int cmd_set(int argc, char **argv)
+static int cmd_property_set(int argc, char **argv)
{
int ret;
char *object = NULL;
@@ -427,12 +428,13 @@ static int cmd_set(int argc, char **argv)
int types = 0;
if (check_argc_min(argc, 4) || check_argc_max(argc, 6))
- usage(cmd_set_usage);
+ usage(cmd_property_set_usage);
- parse_args(argc, argv, cmd_set_usage, &types, &object, &name, &value);
+ parse_args(argc, argv, cmd_property_set_usage, &types,
+ &object, &name, &value);
if (!object || !name || !value) {
fprintf(stderr, "ERROR: invalid arguments.\n");
- usage(cmd_set_usage);
+ usage(cmd_property_set_usage);
}
ret = setget_prop(types, object, name, value);
@@ -440,19 +442,20 @@ static int cmd_set(int argc, char **argv)
return ret;
}
-static int cmd_list(int argc, char **argv)
+static int cmd_property_list(int argc, char **argv)
{
int ret;
char *object = NULL;
int types = 0;
if (check_argc_min(argc, 2) || check_argc_max(argc, 4))
- usage(cmd_list_usage);
+ usage(cmd_property_list_usage);
- parse_args(argc, argv, cmd_list_usage, &types, &object, NULL, NULL);
+ parse_args(argc, argv, cmd_property_list_usage,
+ &types, &object, NULL, NULL);
if (!object) {
fprintf(stderr, "ERROR: invalid arguments.\n");
- usage(cmd_set_usage);
+ usage(cmd_property_set_usage);
}
ret = dump_props(types, object, 1);
@@ -465,10 +468,13 @@ static const char property_cmd_group_info[] =
const struct cmd_group property_cmd_group = {
property_cmd_group_usage, property_cmd_group_info, {
- { "get", cmd_get, cmd_get_usage, NULL, 0 },
- { "set", cmd_set, cmd_set_usage, NULL, 0 },
- { "list", cmd_list, cmd_list_usage, NULL, 0 },
- { 0, 0, 0, 0, 0 },
+ { "get", cmd_property_get,
+ cmd_property_get_usage, NULL, 0 },
+ { "set", cmd_property_set,
+ cmd_property_set_usage, NULL, 0 },
+ { "list", cmd_property_list,
+ cmd_property_list_usage, NULL, 0 },
+ NULL_CMD_STRUCT
}
};
diff --git a/cmds-qgroup.c b/cmds-qgroup.c
index 9a7de34..48c1733 100644
--- a/cmds-qgroup.c
+++ b/cmds-qgroup.c
@@ -37,17 +37,40 @@ static int qgroup_assign(int assign, int argc, char **argv)
int ret = 0;
int fd;
int e;
- char *path = argv[3];
+ int rescan = 0;
+ char *path;
struct btrfs_ioctl_qgroup_assign_args args;
DIR *dirstream = NULL;
- if (check_argc_exact(argc, 4))
+ 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 (check_argc_exact(argc - optind, 3))
return -1;
memset(&args, 0, sizeof(args));
args.assign = assign;
- args.src = parse_qgroupid(argv[1]);
- args.dst = parse_qgroupid(argv[2]);
+ args.src = parse_qgroupid(argv[optind]);
+ args.dst = parse_qgroupid(argv[optind + 1]);
+
+ path = argv[optind + 2];
/*
* FIXME src should accept subvol path
@@ -64,13 +87,38 @@ static int qgroup_assign(int assign, int argc, char **argv)
ret = ioctl(fd, BTRFS_IOC_QGROUP_ASSIGN, &args);
e = errno;
- close_file_or_dir(fd, dirstream);
if (ret < 0) {
fprintf(stderr, "ERROR: unable to assign quota group: %s\n",
strerror(e));
+ close_file_or_dir(fd, dirstream);
return 1;
}
- return 0;
+
+ /*
+ * If ret > 0, it means assign caused qgroup data inconsistent state.
+ * Schedule a quota rescan if requested.
+ *
+ * The return value change only happens in newer kernel. But will not
+ * cause problem since old kernel has a bug that will never clear
+ * INCONSISTENT bit.
+ */
+ if (ret > 0) {
+ if (rescan) {
+ struct btrfs_ioctl_quota_rescan_args args;
+
+ printf("Quota data changed, rescan scheduled\n");
+ memset(&args, 0, sizeof(args));
+ ret = ioctl(fd, BTRFS_IOC_QUOTA_RESCAN, &args);
+ if (ret < 0)
+ fprintf(stderr,
+ "ERROR: quota rescan failed: %s\n",
+ strerror(errno));
+ } else {
+ printf("WARNING: quotas may be inconsistent, rescan needed\n");
+ }
+ }
+ close_file_or_dir(fd, dirstream);
+ return ret;
}
static int qgroup_create(int create, int argc, char **argv)
@@ -157,8 +205,11 @@ static int parse_limit(const char *p, unsigned long long *s)
}
static const char * const cmd_qgroup_assign_usage[] = {
- "btrfs qgroup assign <src> <dst> <path>",
- "Enable subvolume qgroup support for a filesystem.",
+ "btrfs qgroup assign [options] <src> <dst> <path>",
+ "Assign SRC as the child qgroup of DST",
+ "",
+ "--rescan schedule qutoa rescan if needed",
+ "--no-rescan ",
NULL
};
@@ -172,7 +223,7 @@ static int cmd_qgroup_assign(int argc, char **argv)
static const char * const cmd_qgroup_remove_usage[] = {
"btrfs qgroup remove <src> <dst> <path>",
- "Remove a subvol from a quota group.",
+ "Remove a child qgroup SRC from DST.",
NULL
};
@@ -200,7 +251,7 @@ static int cmd_qgroup_create(int argc, char **argv)
static const char * const cmd_qgroup_destroy_usage[] = {
"btrfs qgroup destroy <qgroupid> <path>",
- "Destroy a subvolume quota group.",
+ "Destroy a quota group.",
NULL
};
@@ -224,15 +275,7 @@ static const char * const cmd_qgroup_show_usage[] = {
" (including ancestral qgroups)",
"-f list all qgroups which impact the given path",
" (excluding ancestral qgroups)",
- "--raw raw numbers in bytes",
- "--human-readable",
- " human firendly numbers in given base, 1024 by default",
- "--iec use 1024 as a base (KiB, MiB, GiB, TiB)",
- "--si use 1000 as a base (kB, MB, GB, TB)",
- "--kbytes show sizes in KiB, or kB with --si",
- "--mbytes show sizes in MiB, or MB with --si",
- "--gbytes show sizes in GiB, or GB with --si",
- "--tbytes show sizes in TiB, or TB with --si",
+ HELPINFO_OUTPUT_UNIT,
"--sort=qgroupid,rfer,excl,max_rfer,max_excl",
" list qgroups sorted by specified items",
" you can use '+' or '-' in front of each item.",
@@ -249,27 +292,20 @@ static int cmd_qgroup_show(int argc, char **argv)
DIR *dirstream = NULL;
u64 qgroupid;
int filter_flag = 0;
- unsigned unit_mode = UNITS_DEFAULT;
+ unsigned unit_mode;
struct btrfs_qgroup_comparer_set *comparer_set;
struct btrfs_qgroup_filter_set *filter_set;
filter_set = btrfs_qgroup_alloc_filter_set();
comparer_set = btrfs_qgroup_alloc_comparer_set();
+ unit_mode = get_unit_mode_from_arg(&argc, argv, 0);
+
optind = 1;
while (1) {
int c;
static const struct option long_options[] = {
{"sort", required_argument, NULL, 'S'},
- {"raw", no_argument, NULL, GETOPT_VAL_RAW},
- {"kbytes", no_argument, NULL, GETOPT_VAL_KBYTES},
- {"mbytes", no_argument, NULL, GETOPT_VAL_MBYTES},
- {"gbytes", no_argument, NULL, GETOPT_VAL_GBYTES},
- {"tbytes", no_argument, NULL, GETOPT_VAL_TBYTES},
- {"si", no_argument, NULL, GETOPT_VAL_SI},
- {"iec", no_argument, NULL, GETOPT_VAL_IEC},
- { "human-readable", no_argument, NULL,
- GETOPT_VAL_HUMAN_READABLE},
{ NULL, 0, NULL, 0 }
};
@@ -305,30 +341,6 @@ static int cmd_qgroup_show(int argc, char **argv)
if (ret)
usage(cmd_qgroup_show_usage);
break;
- case GETOPT_VAL_RAW:
- unit_mode = UNITS_RAW;
- break;
- case GETOPT_VAL_KBYTES:
- units_set_base(&unit_mode, UNITS_KBYTES);
- break;
- case GETOPT_VAL_MBYTES:
- units_set_base(&unit_mode, UNITS_MBYTES);
- break;
- case GETOPT_VAL_GBYTES:
- units_set_base(&unit_mode, UNITS_GBYTES);
- break;
- case GETOPT_VAL_TBYTES:
- units_set_base(&unit_mode, UNITS_TBYTES);
- break;
- case GETOPT_VAL_SI:
- units_set_mode(&unit_mode, UNITS_DECIMAL);
- break;
- case GETOPT_VAL_IEC:
- units_set_mode(&unit_mode, UNITS_BINARY);
- break;
- case GETOPT_VAL_HUMAN_READABLE:
- unit_mode = UNITS_HUMAN_BINARY;
- break;
default:
usage(cmd_qgroup_show_usage);
}
@@ -368,7 +380,7 @@ static int cmd_qgroup_show(int argc, char **argv)
static const char * const cmd_qgroup_limit_usage[] = {
"btrfs qgroup limit [options] <size>|none [<qgroupid>] <path>",
- "Limit the size of a subvolume quota group.",
+ "Set the limits a subvolume quota group.",
"",
"-c limit amount of data after compression. This is the default,",
" it is currently not possible to turn off this option.",
diff --git a/cmds-receive.c b/cmds-receive.c
index 071bea9..1d7d897 100644
--- a/cmds-receive.c
+++ b/cmds-receive.c
@@ -17,6 +17,7 @@
*/
#include "kerncompat.h"
+#include "androidcompat.h"
#include <unistd.h>
#include <stdint.h>
@@ -25,9 +26,10 @@
#include <pthread.h>
#include <math.h>
#include <ftw.h>
-#include <wait.h>
+#include <sys/wait.h>
#include <assert.h>
#include <getopt.h>
+#include <limits.h>
#include <sys/stat.h>
#include <sys/types.h>
diff --git a/cmds-replace.c b/cmds-replace.c
index 85365e3..9ab8438 100644
--- a/cmds-replace.c
+++ b/cmds-replace.c
@@ -98,7 +98,7 @@ static int dev_replace_handle_sigint(int fd)
return sigaction(SIGINT, &sa, NULL);
}
-static const char *const cmd_start_replace_usage[] = {
+static const char *const cmd_replace_start_usage[] = {
"btrfs replace start [-Bfr] <srcdev>|<devid> <targetdev> <mount_point>",
"Replace device of a btrfs filesystem.",
"On a live filesystem, duplicate the data to the target device which",
@@ -124,7 +124,7 @@ static const char *const cmd_start_replace_usage[] = {
NULL
};
-static int cmd_start_replace(int argc, char **argv)
+static int cmd_replace_start(int argc, char **argv)
{
struct btrfs_ioctl_dev_replace_args start_args = {0};
struct btrfs_ioctl_dev_replace_args status_args = {0};
@@ -142,6 +142,8 @@ static int cmd_start_replace(int argc, char **argv)
int do_not_background = 0;
int mixed = 0;
DIR *dirstream = NULL;
+ u64 srcdev_size;
+ u64 dstdev_size;
while ((c = getopt(argc, argv, "Brf")) != -1) {
switch (c) {
@@ -156,7 +158,7 @@ static int cmd_start_replace(int argc, char **argv)
break;
case '?':
default:
- usage(cmd_start_replace_usage);
+ usage(cmd_replace_start_usage);
}
}
@@ -165,7 +167,7 @@ static int cmd_start_replace(int argc, char **argv)
BTRFS_IOCTL_DEV_REPLACE_CONT_READING_FROM_SRCDEV_MODE_AVOID :
BTRFS_IOCTL_DEV_REPLACE_CONT_READING_FROM_SRCDEV_MODE_ALWAYS;
if (check_argc_exact(argc - optind, 3))
- usage(cmd_start_replace_usage);
+ usage(cmd_replace_start_usage);
path = argv[optind + 2];
fdmnt = open_path_or_dev_mnt(path, &dirstream);
@@ -243,22 +245,34 @@ static int cmd_start_replace(int argc, char **argv)
for (i = 0; i < fi_args.num_devices; i++)
if (start_args.start.srcdevid == di_args[i].devid)
break;
+ srcdev_size = di_args[i].total_bytes;
free(di_args);
if (i == fi_args.num_devices) {
fprintf(stderr, "Error: '%s' is not a valid devid for filesystem '%s'\n",
srcdev, path);
goto leave_with_error;
}
- } else if (is_block_device(srcdev)) {
+ } else if (is_block_device(srcdev) > 0) {
strncpy((char *)start_args.start.srcdev_name, srcdev,
BTRFS_DEVICE_PATH_NAME_MAX);
start_args.start.srcdevid = 0;
+ srcdev_size = get_partition_size(srcdev);
+ } else {
+ fprintf(stderr, "ERROR: source device must be a block device or a devid\n");
+ goto leave_with_error;
}
ret = test_dev_for_mkfs(dstdev, force_using_targetdev);
if (ret)
goto leave_with_error;
+ dstdev_size = get_partition_size(dstdev);
+ if (srcdev_size > dstdev_size) {
+ fprintf(stderr, "ERROR: target device smaller than source device (required %llu bytes)\n",
+ srcdev_size);
+ goto leave_with_error;
+ }
+
fddstdev = open(dstdev, O_RDWR);
if (fddstdev < 0) {
fprintf(stderr, "Unable to open %s\n", dstdev);
@@ -316,6 +330,7 @@ static int cmd_start_replace(int argc, char **argv)
}
}
close_file_or_dir(fdmnt, dirstream);
+ btrfs_close_all_devices();
return 0;
leave_with_error:
@@ -325,10 +340,11 @@ leave_with_error:
close(fdmnt);
if (fddstdev != -1)
close(fddstdev);
+ btrfs_close_all_devices();
return 1;
}
-static const char *const cmd_status_replace_usage[] = {
+static const char *const cmd_replace_status_usage[] = {
"btrfs replace status [-1] <mount_point>",
"Print status and progress information of a running device replace",
"operation",
@@ -338,7 +354,7 @@ static const char *const cmd_status_replace_usage[] = {
NULL
};
-static int cmd_status_replace(int argc, char **argv)
+static int cmd_replace_status(int argc, char **argv)
{
int fd;
int e;
@@ -355,12 +371,12 @@ static int cmd_status_replace(int argc, char **argv)
break;
case '?':
default:
- usage(cmd_status_replace_usage);
+ usage(cmd_replace_status_usage);
}
}
if (check_argc_exact(argc - optind, 1))
- usage(cmd_status_replace_usage);
+ usage(cmd_replace_status_usage);
path = argv[optind];
fd = open_file_or_dir(path, &dirstream);
@@ -403,7 +419,6 @@ static int print_replace_status(int fd, const char *path, int once)
return ret;
}
- status = &args.status;
if (args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR) {
fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_STATUS) on \"%s\" returns error: %s\n",
path,
@@ -411,6 +426,8 @@ static int print_replace_status(int fd, const char *path, int once)
return -1;
}
+ status = &args.status;
+
skip_stats = 0;
num_chars = 0;
switch (status->replace_state) {
@@ -455,12 +472,10 @@ static int print_replace_status(int fd, const char *path, int once)
printf("Never started");
break;
default:
- prevent_loop = 1;
fprintf(stderr,
- "Unknown btrfs dev replace status:%llu",
- status->replace_state);
- ret = -EINVAL;
- break;
+ "ERROR: ioctl(DEV_REPLACE_STATUS) on \"%s\" got unknown status: %llu\n",
+ path, status->replace_state);
+ return -EINVAL;
}
if (!skip_stats)
@@ -469,9 +484,9 @@ static int print_replace_status(int fd, const char *path, int once)
(unsigned long long)status->num_write_errors,
(unsigned long long)
status->num_uncorrectable_read_errors);
- if (once || prevent_loop || ret) {
+ if (once || prevent_loop) {
printf("\n");
- return ret;
+ break;
}
fflush(stdout);
@@ -507,13 +522,13 @@ progress2string(char *buf, size_t s, int progress_1000)
return buf;
}
-static const char *const cmd_cancel_replace_usage[] = {
+static const char *const cmd_replace_cancel_usage[] = {
"btrfs replace cancel <mount_point>",
"Cancel a running device replace operation.",
NULL
};
-static int cmd_cancel_replace(int argc, char **argv)
+static int cmd_replace_cancel(int argc, char **argv)
{
struct btrfs_ioctl_dev_replace_args args = {0};
int ret;
@@ -527,12 +542,12 @@ static int cmd_cancel_replace(int argc, char **argv)
switch (c) {
case '?':
default:
- usage(cmd_cancel_replace_usage);
+ usage(cmd_replace_cancel_usage);
}
}
if (check_argc_exact(argc - optind, 1))
- usage(cmd_cancel_replace_usage);
+ usage(cmd_replace_cancel_usage);
path = argv[optind];
fd = open_file_or_dir(path, &dirstream);
@@ -570,11 +585,11 @@ static const char replace_cmd_group_info[] =
const struct cmd_group replace_cmd_group = {
replace_cmd_group_usage, replace_cmd_group_info, {
- { "start", cmd_start_replace, cmd_start_replace_usage, NULL,
+ { "start", cmd_replace_start, cmd_replace_start_usage, NULL,
0 },
- { "status", cmd_status_replace, cmd_status_replace_usage, NULL,
+ { "status", cmd_replace_status, cmd_replace_status_usage, NULL,
0 },
- { "cancel", cmd_cancel_replace, cmd_cancel_replace_usage, NULL,
+ { "cancel", cmd_replace_cancel, cmd_replace_cancel_usage, NULL,
0 },
NULL_CMD_STRUCT
}
diff --git a/cmds-rescue.c b/cmds-rescue.c
index a82da16..fb3227b 100644
--- a/cmds-rescue.c
+++ b/cmds-rescue.c
@@ -33,7 +33,7 @@ static const char * const rescue_cmd_group_usage[] = {
int btrfs_recover_chunk_tree(char *path, int verbose, int yes);
int btrfs_recover_superblocks(char *path, int verbose, int yes);
-const char * const cmd_chunk_recover_usage[] = {
+static const char * const cmd_rescue_chunk_recover_usage[] = {
"btrfs rescue chunk-recover [options] <device>",
"Recover the chunk tree by scanning the devices one by one.",
"",
@@ -43,7 +43,7 @@ const char * const cmd_chunk_recover_usage[] = {
NULL
};
-const char * const cmd_super_recover_usage[] = {
+static const char * const cmd_rescue_super_recover_usage[] = {
"btrfs rescue super-recover [options] <device>",
"Recover bad superblocks from good copies",
"",
@@ -52,7 +52,7 @@ const char * const cmd_super_recover_usage[] = {
NULL
};
-int cmd_chunk_recover(int argc, char *argv[])
+static int cmd_rescue_chunk_recover(int argc, char *argv[])
{
int ret = 0;
char *file;
@@ -72,13 +72,13 @@ int cmd_chunk_recover(int argc, char *argv[])
break;
case 'h':
default:
- usage(cmd_chunk_recover_usage);
+ usage(cmd_rescue_chunk_recover_usage);
}
}
argc = argc - optind;
if (check_argc_exact(argc, 1))
- usage(cmd_chunk_recover_usage);
+ usage(cmd_rescue_chunk_recover_usage);
file = argv[optind];
@@ -112,7 +112,7 @@ int cmd_chunk_recover(int argc, char *argv[])
* 3 : Fail to Recover bad supeblocks
* 4 : Abort to recover bad superblocks
*/
-int cmd_super_recover(int argc, char **argv)
+static int cmd_rescue_super_recover(int argc, char **argv)
{
int ret;
int verbose = 0;
@@ -131,12 +131,12 @@ int cmd_super_recover(int argc, char **argv)
yes = 1;
break;
default:
- usage(cmd_super_recover_usage);
+ usage(cmd_rescue_super_recover_usage);
}
}
argc = argc - optind;
if (check_argc_exact(argc, 1))
- usage(cmd_super_recover_usage);
+ usage(cmd_rescue_super_recover_usage);
dname = argv[optind];
ret = check_mounted(dname);
@@ -152,14 +152,14 @@ int cmd_super_recover(int argc, char **argv)
return ret;
}
-const char * const cmd_rescue_zero_log_usage[] = {
+static const char * const cmd_rescue_zero_log_usage[] = {
"btrfs rescue zero-log <device>",
"Clear the tree log. Usable if it's corrupted and prevents mount.",
"",
NULL
};
-int cmd_rescue_zero_log(int argc, char **argv)
+static int cmd_rescue_zero_log(int argc, char **argv)
{
struct btrfs_root *root;
struct btrfs_trans_handle *trans;
@@ -206,8 +206,10 @@ static const char rescue_cmd_group_info[] =
const struct cmd_group rescue_cmd_group = {
rescue_cmd_group_usage, rescue_cmd_group_info, {
- { "chunk-recover", cmd_chunk_recover, cmd_chunk_recover_usage, NULL, 0},
- { "super-recover", cmd_super_recover, cmd_super_recover_usage, NULL, 0},
+ { "chunk-recover", cmd_rescue_chunk_recover,
+ cmd_rescue_chunk_recover_usage, NULL, 0},
+ { "super-recover", cmd_rescue_super_recover,
+ cmd_rescue_super_recover_usage, NULL, 0},
{ "zero-log", cmd_rescue_zero_log, cmd_rescue_zero_log_usage, NULL, 0},
NULL_CMD_STRUCT
}
diff --git a/cmds-scrub.c b/cmds-scrub.c
index b7aa809..3ebda9d 100644
--- a/cmds-scrub.c
+++ b/cmds-scrub.c
@@ -17,6 +17,7 @@
*/
#include "kerncompat.h"
+#include "androidcompat.h"
#include <sys/ioctl.h>
#include <sys/wait.h>
@@ -34,6 +35,7 @@
#include <ctype.h>
#include <signal.h>
#include <stdarg.h>
+#include <limits.h>
#include "ctree.h"
#include "ioctl.h"
@@ -252,17 +254,15 @@ static void _print_scrub_ss(struct scrub_stats *ss)
hours = ss->duration / (60 * 60);
gmtime_r(&seconds, &tm);
strftime(t, sizeof(t), "%M:%S", &tm);
- if (ss->finished && !ss->canceled) {
- printf(" and finished after %02u:%s\n", hours, t);
- } else if (ss->canceled) {
+ if (ss->in_progress)
+ printf(", running for %02u:%s\n", hours, t);
+ else if (ss->canceled)
printf(" and was aborted after %02u:%s\n", hours, t);
- } else {
- if (ss->in_progress)
- printf(", running for %02u:%s\n", hours, t);
- else
- printf(", interrupted after %02u:%s, not running\n",
- hours, t);
- }
+ else if (ss->finished)
+ printf(" and finished after %02u:%s\n", hours, t);
+ else
+ printf(", interrupted after %02u:%s, not running\n",
+ hours, t);
}
static void print_scrub_dev(struct btrfs_ioctl_dev_info_args *di,
@@ -501,12 +501,16 @@ again:
}
return p;
}
- if (avail == -1)
+ if (avail == -1) {
+ free_history(p);
return ERR_PTR(-errno);
+ }
avail += old_avail;
i = 0;
while (i < avail) {
+ void *tmp;
+
switch (state) {
case 0: /* start of file */
ret = scrub_kvread(&i,
@@ -533,11 +537,17 @@ again:
continue;
}
++curr;
+ tmp = p;
p = realloc(p, (curr + 2) * sizeof(*p));
- if (p)
- p[curr] = malloc(sizeof(**p));
- if (!p || !p[curr])
+ if (!p) {
+ free_history(tmp);
return ERR_PTR(-errno);
+ }
+ p[curr] = malloc(sizeof(**p));
+ if (!p[curr]) {
+ free_history(p);
+ return ERR_PTR(-errno);
+ }
memset(p[curr], 0, sizeof(**p));
p[curr + 1] = NULL;
++state;
diff --git a/cmds-send.c b/cmds-send.c
index 20bba18..581b25e 100644
--- a/cmds-send.c
+++ b/cmds-send.c
@@ -33,6 +33,7 @@
#include <assert.h>
#include <getopt.h>
#include <uuid/uuid.h>
+#include <limits.h>
#include "ctree.h"
#include "ioctl.h"
@@ -173,11 +174,16 @@ out:
static int add_clone_source(struct btrfs_send *s, u64 root_id)
{
+ void *tmp;
+
+ tmp = s->clone_sources;
s->clone_sources = realloc(s->clone_sources,
sizeof(*s->clone_sources) * (s->clone_sources_count + 1));
- if (!s->clone_sources)
+ if (!s->clone_sources) {
+ free(tmp);
return -ENOMEM;
+ }
s->clone_sources[s->clone_sources_count++] = root_id;
return 0;
@@ -192,13 +198,13 @@ static int write_buf(int fd, const void *buf, int size)
ret = write(fd, (char*)buf + pos, size - pos);
if (ret < 0) {
ret = -errno;
- fprintf(stderr, "ERROR: failed to dump stream. %s",
+ fprintf(stderr, "ERROR: failed to dump stream. %s\n",
strerror(-ret));
goto out;
}
if (!ret) {
ret = -EIO;
- fprintf(stderr, "ERROR: failed to dump stream. %s",
+ fprintf(stderr, "ERROR: failed to dump stream. %s\n",
strerror(-ret));
goto out;
}
diff --git a/cmds-subvolume.c b/cmds-subvolume.c
index e88a6e6..82173c0 100644
--- a/cmds-subvolume.c
+++ b/cmds-subvolume.c
@@ -66,12 +66,12 @@ static int is_subvolume_cleaned(int fd, u64 subvolid)
static int wait_for_subvolume_cleaning(int fd, int count, u64 *ids,
int sleep_interval)
{
- int ret = 0;
- int remaining;
+ int ret;
int i;
- remaining = count;
while (1) {
+ int clean = 1;
+
for (i = 0; i < count; i++) {
if (!ids[i])
continue;
@@ -80,20 +80,21 @@ static int wait_for_subvolume_cleaning(int fd, int count, u64 *ids,
fprintf(stderr,
"ERROR: can't perform the search - %s\n",
strerror(-ret));
- goto out;
+ return ret;
}
if (ret) {
printf("Subvolume id %llu is gone\n", ids[i]);
ids[i] = 0;
- remaining--;
+ } else {
+ clean = 0;
}
}
- if (!remaining)
+ if (clean == 0)
break;
sleep(sleep_interval);
}
-out:
- return ret;
+
+ return 0;
}
static const char * const subvolume_cmd_group_usage[] = {
@@ -611,7 +612,7 @@ out:
return !!ret;
}
-static const char * const cmd_snapshot_usage[] = {
+static const char * const cmd_subvol_snapshot_usage[] = {
"btrfs subvolume snapshot [-r] [-i <qgroupid>] <source> <dest>|[<dest>/]<name>",
"Create a snapshot of the subvolume",
"Create a writable/readonly snapshot of the subvolume <source> with",
@@ -624,7 +625,7 @@ static const char * const cmd_snapshot_usage[] = {
NULL
};
-static int cmd_snapshot(int argc, char **argv)
+static int cmd_subvol_snapshot(int argc, char **argv)
{
char *subvol, *dst;
int res, retval;
@@ -671,12 +672,12 @@ static int cmd_snapshot(int argc, char **argv)
}
break;
default:
- usage(cmd_snapshot_usage);
+ usage(cmd_subvol_snapshot_usage);
}
}
if (check_argc_exact(argc - optind, 2))
- usage(cmd_snapshot_usage);
+ usage(cmd_subvol_snapshot_usage);
subvol = argv[optind];
dst = argv[optind + 1];
@@ -875,13 +876,13 @@ static int cmd_subvol_set_default(int argc, char **argv)
return 0;
}
-static const char * const cmd_find_new_usage[] = {
+static const char * const cmd_subvol_find_new_usage[] = {
"btrfs subvolume find-new <path> <lastgen>",
"List the recently modified files in a filesystem",
NULL
};
-static int cmd_find_new(int argc, char **argv)
+static int cmd_subvol_find_new(int argc, char **argv)
{
int fd;
int ret;
@@ -890,7 +891,7 @@ static int cmd_find_new(int argc, char **argv)
DIR *dirstream = NULL;
if (check_argc_exact(argc, 3))
- usage(cmd_find_new_usage);
+ usage(cmd_subvol_find_new_usage);
subvol = argv[1];
last_gen = arg_strtou64(argv[2]);
@@ -938,7 +939,7 @@ static int cmd_subvol_show(int argc, char **argv)
char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
char *fullpath = NULL, *svpath = NULL, *mnt = NULL;
char raw_prefix[] = "\t\t\t\t";
- u64 sv_id, mntid;
+ u64 sv_id;
int fd = -1, mntfd = -1;
int ret = 1;
DIR *dirstream1 = NULL, *dirstream2 = NULL;
@@ -998,12 +999,6 @@ static int cmd_subvol_show(int argc, char **argv)
goto out;
}
- ret = btrfs_list_get_path_rootid(mntfd, &mntid);
- if (ret) {
- fprintf(stderr, "ERROR: can't get rootid for '%s'\n", mnt);
- goto out;
- }
-
if (sv_id == BTRFS_FS_TREE_OBJECTID) {
printf("%s is btrfs root\n", fullpath);
goto out;
@@ -1169,12 +1164,13 @@ again:
* Enumerate all dead subvolumes that exist in the filesystem.
* Fill @ids and reallocate to bigger size if needed.
*/
-static int enumerate_dead_subvols(int fd, int count, u64 **ids)
+static int enumerate_dead_subvols(int fd, u64 **ids)
{
int ret;
struct btrfs_ioctl_search_args args;
struct btrfs_ioctl_search_key *sk = &args.key;
int idx = 0;
+ int count = 0;
memset(&args, 0, sizeof(args));
@@ -1189,6 +1185,7 @@ static int enumerate_dead_subvols(int fd, int count, u64 **ids)
sk->max_transid = (u64)-1;
sk->nr_items = 4096;
+ *ids = NULL;
while (1) {
struct btrfs_ioctl_search_header *sh;
unsigned long off;
@@ -1207,8 +1204,6 @@ static int enumerate_dead_subvols(int fd, int count, u64 **ids)
off += sizeof(*sh);
if (sh->type == BTRFS_ORPHAN_ITEM_KEY) {
- *ids[idx] = sh->offset;
- idx++;
if (idx >= count) {
u64 *newids;
@@ -1218,6 +1213,8 @@ static int enumerate_dead_subvols(int fd, int count, u64 **ids)
return -ENOMEM;
*ids = newids;
}
+ (*ids)[idx] = sh->offset;
+ idx++;
}
off += sh->len;
@@ -1284,14 +1281,7 @@ static int cmd_subvol_sync(int argc, char **argv)
id_count = argc - optind;
if (!id_count) {
- id_count = SUBVOL_ID_BATCH;
- ids = (u64*)malloc(id_count * sizeof(u64));
- if (!ids) {
- fprintf(stderr, "ERROR: not enough memory\n");
- ret = 1;
- goto out;
- }
- id_count = enumerate_dead_subvols(fd, id_count, &ids);
+ id_count = enumerate_dead_subvols(fd, &ids);
if (id_count < 0) {
fprintf(stderr, "ERROR: can't enumerate dead subvolumes: %s\n",
strerror(-id_count));
@@ -1353,12 +1343,14 @@ const struct cmd_group subvolume_cmd_group = {
{ "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
{ "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
{ "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
- { "snapshot", cmd_snapshot, cmd_snapshot_usage, NULL, 0 },
+ { "snapshot", cmd_subvol_snapshot, cmd_subvol_snapshot_usage,
+ NULL, 0 },
{ "get-default", cmd_subvol_get_default,
cmd_subvol_get_default_usage, NULL, 0 },
{ "set-default", cmd_subvol_set_default,
cmd_subvol_set_default_usage, NULL, 0 },
- { "find-new", cmd_find_new, cmd_find_new_usage, NULL, 0 },
+ { "find-new", cmd_subvol_find_new, cmd_subvol_find_new_usage,
+ NULL, 0 },
{ "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
{ "sync", cmd_subvol_sync, cmd_subvol_sync_usage, NULL, 0 },
NULL_CMD_STRUCT
diff --git a/configure b/configure
index 034a8ab..fd7e3f5 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.1.2.
+# Generated by GNU Autoconf 2.69 for btrfs-progs v4.2.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.1.2'
-PACKAGE_STRING='btrfs-progs v4.1.2'
+PACKAGE_VERSION='v4.2.2'
+PACKAGE_STRING='btrfs-progs v4.2.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.1.2 to adapt to many kinds of systems.
+\`configure' configures btrfs-progs v4.2.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.1.2:";;
+ short | recursive ) echo "Configuration of btrfs-progs v4.2.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.1.2
+btrfs-progs configure v4.2.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.1.2, which was
+It was created by btrfs-progs $as_me v4.2.2, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
@@ -6375,7 +6375,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.1.2, which was
+This file was extended by btrfs-progs $as_me v4.2.2, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -6438,7 +6438,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.1.2
+btrfs-progs config.status v4.2.2
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
diff --git a/ctree.h b/ctree.h
index 5550d45..c57f9ca 100644
--- a/ctree.h
+++ b/ctree.h
@@ -946,7 +946,7 @@ struct btrfs_block_group_cache {
struct btrfs_extent_ops {
int (*alloc_extent)(struct btrfs_root *root, u64 num_bytes,
- u64 hint_byte, struct btrfs_key *ins);
+ u64 hint_byte, struct btrfs_key *ins, int metadata);
int (*free_extent)(struct btrfs_root *root, u64 bytenr,
u64 num_bytes);
};
@@ -1423,7 +1423,9 @@ BTRFS_SETGET_FUNCS(inode_flags, struct btrfs_inode_item, flags, 64);
BTRFS_SETGET_STACK_FUNCS(stack_inode_generation,
struct btrfs_inode_item, generation, 64);
BTRFS_SETGET_STACK_FUNCS(stack_inode_sequence,
- struct btrfs_inode_item, generation, 64);
+ struct btrfs_inode_item, sequence, 64);
+BTRFS_SETGET_STACK_FUNCS(stack_inode_transid,
+ struct btrfs_inode_item, transid, 64);
BTRFS_SETGET_STACK_FUNCS(stack_inode_size,
struct btrfs_inode_item, size, 64);
BTRFS_SETGET_STACK_FUNCS(stack_inode_nbytes,
@@ -1491,6 +1493,9 @@ BTRFS_SETGET_FUNCS(dev_extent_chunk_offset, struct btrfs_dev_extent,
chunk_offset, 64);
BTRFS_SETGET_FUNCS(dev_extent_length, struct btrfs_dev_extent, length, 64);
+BTRFS_SETGET_STACK_FUNCS(stack_dev_extent_length, struct btrfs_dev_extent,
+ length, 64);
+
static inline u8 *btrfs_dev_extent_chunk_tree_uuid(struct btrfs_dev_extent *dev)
{
unsigned long ptr = offsetof(struct btrfs_dev_extent, chunk_tree_uuid);
diff --git a/debian/changelog b/debian/changelog
index b284551..d61e625 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+btrfs-tools (4.2.2-1) unstable; urgency=medium
+
+ * New upstream release.
+
+ -- Dimitri John Ledkov <dimitri.j.ledkov@linux.intel.com> Sat, 24 Oct 2015 00:09:28 +0100
+
btrfs-tools (4.1.2-1) unstable; urgency=medium
* New upstream release.
diff --git a/dir-item.c b/dir-item.c
index a5bf861..bc59d17 100644
--- a/dir-item.c
+++ b/dir-item.c
@@ -277,7 +277,7 @@ int btrfs_delete_one_dir_name(struct btrfs_trans_handle *trans,
return ret;
}
-int verify_dir_item(struct btrfs_root *root,
+static int verify_dir_item(struct btrfs_root *root,
struct extent_buffer *leaf,
struct btrfs_dir_item *dir_item)
{
@@ -285,7 +285,7 @@ int verify_dir_item(struct btrfs_root *root,
u8 type = btrfs_dir_type(leaf, dir_item);
if (type >= BTRFS_FT_MAX) {
- fprintf(stderr, "invalid dir item type: %d",
+ fprintf(stderr, "invalid dir item type: %d\n",
(int)type);
return 1;
}
@@ -294,7 +294,7 @@ int verify_dir_item(struct btrfs_root *root,
namelen = XATTR_NAME_MAX;
if (btrfs_dir_name_len(leaf, dir_item) > namelen) {
- fprintf(stderr, "invalid dir item name len: %u",
+ fprintf(stderr, "invalid dir item name len: %u\n",
(unsigned)btrfs_dir_data_len(leaf, dir_item));
return 1;
}
@@ -302,7 +302,7 @@ int verify_dir_item(struct btrfs_root *root,
/* BTRFS_MAX_XATTR_SIZE is the same for all dir items */
if ((btrfs_dir_data_len(leaf, dir_item) +
btrfs_dir_name_len(leaf, dir_item)) > BTRFS_MAX_XATTR_SIZE(root)) {
- fprintf(stderr, "invalid dir item name + data len: %u + %u",
+ fprintf(stderr, "invalid dir item name + data len: %u + %u\n",
(unsigned)btrfs_dir_name_len(leaf, dir_item),
(unsigned)btrfs_dir_data_len(leaf, dir_item));
return 1;
diff --git a/disk-io.c b/disk-io.c
index fdcfd6d..c3da18c 100644
--- a/disk-io.c
+++ b/disk-io.c
@@ -266,7 +266,7 @@ int read_whole_eb(struct btrfs_fs_info *info, struct extent_buffer *eb, int mirr
}
device = multi->stripes[0].dev;
- if (device->fd == 0) {
+ if (device->fd <= 0) {
kfree(multi);
return -EIO;
}
@@ -385,7 +385,7 @@ int read_extent_data(struct btrfs_root *root, char *data,
}
device = multi->stripes[0].dev;
- if (device->fd == 0)
+ if (device->fd <= 0)
goto err;
if (*len > max_len)
*len = max_len;
@@ -428,6 +428,7 @@ int write_and_map_eb(struct btrfs_trans_handle *trans,
ret = write_extent_to_disk(eb);
BUG_ON(ret);
}
+ kfree(raid_map);
kfree(multi);
return 0;
}
@@ -832,13 +833,13 @@ struct btrfs_fs_info *btrfs_new_fs_info(int writable, u64 sb_bytenr)
memset(fs_info, 0, sizeof(struct btrfs_fs_info));
- fs_info->tree_root = malloc(sizeof(struct btrfs_root));
- fs_info->extent_root = malloc(sizeof(struct btrfs_root));
- fs_info->chunk_root = malloc(sizeof(struct btrfs_root));
- fs_info->dev_root = malloc(sizeof(struct btrfs_root));
- fs_info->csum_root = malloc(sizeof(struct btrfs_root));
- fs_info->quota_root = malloc(sizeof(struct btrfs_root));
- fs_info->super_copy = malloc(BTRFS_SUPER_INFO_SIZE);
+ fs_info->tree_root = calloc(1, sizeof(struct btrfs_root));
+ fs_info->extent_root = calloc(1, sizeof(struct btrfs_root));
+ fs_info->chunk_root = calloc(1, sizeof(struct btrfs_root));
+ fs_info->dev_root = calloc(1, sizeof(struct btrfs_root));
+ fs_info->csum_root = calloc(1, sizeof(struct btrfs_root));
+ fs_info->quota_root = calloc(1, sizeof(struct btrfs_root));
+ fs_info->super_copy = calloc(1, BTRFS_SUPER_INFO_SIZE);
if (!fs_info->tree_root || !fs_info->extent_root ||
!fs_info->chunk_root || !fs_info->dev_root ||
@@ -846,14 +847,6 @@ struct btrfs_fs_info *btrfs_new_fs_info(int writable, u64 sb_bytenr)
!fs_info->super_copy)
goto free_all;
- memset(fs_info->super_copy, 0, BTRFS_SUPER_INFO_SIZE);
- memset(fs_info->tree_root, 0, sizeof(struct btrfs_root));
- memset(fs_info->extent_root, 0, sizeof(struct btrfs_root));
- memset(fs_info->chunk_root, 0, sizeof(struct btrfs_root));
- memset(fs_info->dev_root, 0, sizeof(struct btrfs_root));
- memset(fs_info->csum_root, 0, sizeof(struct btrfs_root));
- memset(fs_info->quota_root, 0, sizeof(struct btrfs_root));
-
extent_io_tree_init(&fs_info->extent_cache);
extent_io_tree_init(&fs_info->free_space_cache);
extent_io_tree_init(&fs_info->block_group_cache);
@@ -1419,7 +1412,8 @@ static int write_dev_supers(struct btrfs_root *root,
ret = pwrite64(device->fd, root->fs_info->super_copy,
BTRFS_SUPER_INFO_SIZE,
root->fs_info->super_bytenr);
- BUG_ON(ret != BTRFS_SUPER_INFO_SIZE);
+ if (ret != BTRFS_SUPER_INFO_SIZE)
+ goto write_err;
return 0;
}
@@ -1441,10 +1435,19 @@ static int write_dev_supers(struct btrfs_root *root,
*/
ret = pwrite64(device->fd, root->fs_info->super_copy,
BTRFS_SUPER_INFO_SIZE, bytenr);
- BUG_ON(ret != BTRFS_SUPER_INFO_SIZE);
+ if (ret != BTRFS_SUPER_INFO_SIZE)
+ goto write_err;
}
return 0;
+
+write_err:
+ if (ret > 0)
+ fprintf(stderr, "WARNING: failed to write all sb data\n");
+ else
+ fprintf(stderr, "WARNING: failed to write sb: %s\n",
+ strerror(errno));
+ return ret;
}
int write_all_supers(struct btrfs_root *root)
diff --git a/extent-tree.c b/extent-tree.c
index ac582e0..0c8152a 100644
--- a/extent-tree.c
+++ b/extent-tree.c
@@ -2605,6 +2605,11 @@ check_failed:
}
if (!(data & BTRFS_BLOCK_GROUP_DATA)) {
+ if (check_crossing_stripes(ins->objectid, num_bytes)) {
+ search_start = round_down(ins->objectid + num_bytes,
+ BTRFS_STRIPE_LEN);
+ goto new_group;
+ }
block_group = btrfs_lookup_block_group(info, ins->objectid);
if (block_group)
trans->block_group = block_group;
@@ -2649,7 +2654,7 @@ int btrfs_reserve_extent(struct btrfs_trans_handle *trans,
if (info->extent_ops) {
struct btrfs_extent_ops *ops = info->extent_ops;
- ret = ops->alloc_extent(root, num_bytes, hint_byte, ins);
+ ret = ops->alloc_extent(root, num_bytes, hint_byte, ins, !data);
BUG_ON(ret);
goto found;
}
diff --git a/extent_io.c b/extent_io.c
index 5d49710..07695ef 100644
--- a/extent_io.c
+++ b/extent_io.c
@@ -714,7 +714,7 @@ int read_data_from_disk(struct btrfs_fs_info *info, void *buf, u64 offset,
device = multi->stripes[0].dev;
read_len = min(bytes_left, read_len);
- if (device->fd == 0) {
+ if (device->fd <= 0) {
kfree(multi);
return -EIO;
}
@@ -790,7 +790,7 @@ int write_data_to_disk(struct btrfs_fs_info *info, void *buf, u64 offset,
raid_map = NULL;
} else while (dev_nr < multi->num_stripes) {
device = multi->stripes[dev_nr].dev;
- if (device->fd == 0) {
+ if (device->fd <= 0) {
kfree(multi);
return -EIO;
}
diff --git a/find-root.c b/find-root.c
index 1af37b5..55e7942 100644
--- a/find-root.c
+++ b/find-root.c
@@ -46,9 +46,14 @@ static int add_eb_to_result(struct extent_buffer *eb,
generation < filter->generation)
return ret;
- /* Get the generation cache or create one */
+ /*
+ * Get the generation cache or create one
+ *
+ * NOTE: search_cache_extent() may return cache that doesn't cover
+ * the range. So we need an extra check to make sure it's the right one.
+ */
cache = search_cache_extent(result, generation);
- if (!cache) {
+ if (!cache || cache->start != generation) {
gen_cache = malloc(sizeof(*gen_cache));
BUG_ON(!gen_cache);
cache = &gen_cache->cache;
diff --git a/free-space-cache.c b/free-space-cache.c
index 67f00fd..19ab0c9 100644
--- a/free-space-cache.c
+++ b/free-space-cache.c
@@ -107,7 +107,8 @@ static int io_ctl_prepare_pages(struct io_ctl *io_ctl, struct btrfs_root *root,
ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
if (ret) {
- printf("Couldn't find file extent item for free space inode"
+ fprintf(stderr,
+ "Couldn't find file extent item for free space inode"
" %Lu\n", ino);
btrfs_release_path(path);
return -EINVAL;
@@ -138,7 +139,7 @@ static int io_ctl_prepare_pages(struct io_ctl *io_ctl, struct btrfs_root *root,
struct btrfs_file_extent_item);
if (btrfs_file_extent_type(path->nodes[0], fi) !=
BTRFS_FILE_EXTENT_REG) {
- printf("Not the file extent type we wanted\n");
+ fprintf(stderr, "Not the file extent type we wanted\n");
ret = -EINVAL;
break;
}
@@ -307,7 +308,7 @@ static int __load_free_space_cache(struct btrfs_root *root,
ret = btrfs_search_slot(NULL, root, &inode_location, path, 0, 0);
if (ret) {
- printf("Couldn't find free space inode %d\n", ret);
+ fprintf(stderr, "Couldn't find free space inode %d\n", ret);
return 0;
}
@@ -322,7 +323,8 @@ static int __load_free_space_cache(struct btrfs_root *root,
}
if (btrfs_inode_generation(leaf, inode_item) != generation) {
- printf("free space inode generation (%llu) did not match "
+ fprintf(stderr,
+ "free space inode generation (%llu) did not match "
"free space cache generation (%llu)\n",
(unsigned long long)btrfs_inode_generation(leaf,
inode_item),
@@ -372,7 +374,8 @@ static int __load_free_space_cache(struct btrfs_root *root,
if (type == BTRFS_FREE_SPACE_EXTENT) {
ret = link_free_space(ctl, e);
if (ret) {
- printf("Duplicate entries in free space cache, dumping");
+ fprintf(stderr,
+ "Duplicate entries in free space cache\n");
free(e);
goto free_cache;
}
@@ -387,7 +390,8 @@ static int __load_free_space_cache(struct btrfs_root *root,
ret = link_free_space(ctl, e);
ctl->total_bitmaps++;
if (ret) {
- printf("Duplicate entries in free space cache, dumping");
+ fprintf(stderr,
+ "Duplicate entries in free space cache\n");
free(e->bitmap);
free(e);
goto free_cache;
@@ -444,7 +448,8 @@ int load_free_space_cache(struct btrfs_fs_info *fs_info,
block_group->bytes_super));
if (ret == 1 && !matched) {
__btrfs_remove_free_space_cache(ctl);
- printf("block group %llu has wrong amount of free space",
+ fprintf(stderr,
+ "block group %llu has wrong amount of free space\n",
block_group->key.objectid);
ret = -1;
}
@@ -452,8 +457,9 @@ int load_free_space_cache(struct btrfs_fs_info *fs_info,
if (ret < 0) {
ret = 0;
- printf("failed to load free space cache for block group %llu\n",
- block_group->key.objectid);
+ fprintf(stderr,
+ "failed to load free space cache for block group %llu\n",
+ block_group->key.objectid);
}
return ret;
diff --git a/kerncompat.h b/kerncompat.h
index 5d92856..7c627ba 100644
--- a/kerncompat.h
+++ b/kerncompat.h
@@ -33,7 +33,9 @@
#include <features.h>
#ifndef __GLIBC__
+#ifndef BTRFS_DISABLE_BACKTRACE
#define BTRFS_DISABLE_BACKTRACE
+#endif
#define __always_inline __inline __attribute__ ((__always_inline__))
#endif
diff --git a/mkfs.c b/mkfs.c
index dafd500..a5802f7 100644
--- a/mkfs.c
+++ b/mkfs.c
@@ -17,6 +17,7 @@
*/
#include "kerncompat.h"
+#include "androidcompat.h"
#include <sys/ioctl.h>
#include <sys/mount.h>
@@ -25,7 +26,7 @@
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
-#include <sys/dir.h>
+/* #include <sys/dir.h> included via androidcompat.h */
#include <fcntl.h>
#include <unistd.h>
#include <getopt.h>
@@ -356,6 +357,7 @@ static u64 parse_profile(char *s)
return 0;
} else {
fprintf(stderr, "Unknown profile %s\n", s);
+ exit(1);
}
/* not reached */
return 0;
@@ -599,7 +601,7 @@ static int add_symbolic_link(struct btrfs_trans_handle *trans,
goto fail;
}
if (ret >= sectorsize) {
- fprintf(stderr, "symlink too long for %s", path_name);
+ fprintf(stderr, "symlink too long for %s\n", path_name);
ret = -1;
goto fail;
}
@@ -1177,6 +1179,149 @@ static void list_all_devices(struct btrfs_root *root)
printf("\n");
}
+static int is_temp_block_group(struct extent_buffer *node,
+ struct btrfs_block_group_item *bgi,
+ u64 data_profile, u64 meta_profile,
+ u64 sys_profile)
+{
+ u64 flag = btrfs_disk_block_group_flags(node, bgi);
+ u64 flag_type = flag & BTRFS_BLOCK_GROUP_TYPE_MASK;
+ u64 flag_profile = flag & BTRFS_BLOCK_GROUP_PROFILE_MASK;
+ u64 used = btrfs_disk_block_group_used(node, bgi);
+
+ /*
+ * Chunks meets all the following conditions is a temp chunk
+ * 1) Empty chunk
+ * Temp chunk is always empty.
+ *
+ * 2) profile dismatch with mkfs profile.
+ * Temp chunk is always in SINGLE
+ *
+ * 3) Size differs with mkfs_alloc
+ * Special case for SINGLE/SINGLE btrfs.
+ * In that case, temp data chunk and real data chunk are always empty.
+ * So we need to use mkfs_alloc to be sure which chunk is the newly
+ * allocated.
+ *
+ * Normally, new chunk size is equal to mkfs one (One chunk)
+ * If it has multiple chunks, we just refuse to delete any one.
+ * As they are all single, so no real problem will happen.
+ * So only use condition 1) and 2) to judge them.
+ */
+ if (used != 0)
+ return 0;
+ switch (flag_type) {
+ case BTRFS_BLOCK_GROUP_DATA:
+ case BTRFS_BLOCK_GROUP_DATA | BTRFS_BLOCK_GROUP_METADATA:
+ data_profile &= BTRFS_BLOCK_GROUP_PROFILE_MASK;
+ if (flag_profile != data_profile)
+ return 1;
+ break;
+ case BTRFS_BLOCK_GROUP_METADATA:
+ meta_profile &= BTRFS_BLOCK_GROUP_PROFILE_MASK;
+ if (flag_profile != meta_profile)
+ return 1;
+ break;
+ case BTRFS_BLOCK_GROUP_SYSTEM:
+ sys_profile &= BTRFS_BLOCK_GROUP_PROFILE_MASK;
+ if (flag_profile != sys_profile)
+ return 1;
+ break;
+ }
+ return 0;
+}
+
+/* Note: if current is a block group, it will skip it anyway */
+static int next_block_group(struct btrfs_root *root,
+ struct btrfs_path *path)
+{
+ struct btrfs_key key;
+ int ret = 0;
+
+ while (1) {
+ ret = btrfs_next_item(root, path);
+ if (ret)
+ goto out;
+
+ btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
+ if (key.type == BTRFS_BLOCK_GROUP_ITEM_KEY)
+ goto out;
+ }
+out:
+ return ret;
+}
+
+/* This function will cleanup */
+static int cleanup_temp_chunks(struct btrfs_fs_info *fs_info,
+ struct mkfs_allocation *alloc,
+ u64 data_profile, u64 meta_profile,
+ u64 sys_profile)
+{
+ struct btrfs_trans_handle *trans = NULL;
+ struct btrfs_block_group_item *bgi;
+ struct btrfs_root *root = fs_info->extent_root;
+ struct btrfs_key key;
+ struct btrfs_key found_key;
+ struct btrfs_path *path;
+ int ret = 0;
+
+ path = btrfs_alloc_path();
+ if (!path) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ trans = btrfs_start_transaction(root, 1);
+
+ key.objectid = 0;
+ key.type = BTRFS_BLOCK_GROUP_ITEM_KEY;
+ key.offset = 0;
+
+ while (1) {
+ /*
+ * as the rest of the loop may modify the tree, we need to
+ * start a new search each time.
+ */
+ ret = btrfs_search_slot(trans, root, &key, path, 0, 0);
+ if (ret < 0)
+ goto out;
+
+ btrfs_item_key_to_cpu(path->nodes[0], &found_key,
+ path->slots[0]);
+ if (found_key.objectid < key.objectid)
+ goto out;
+ if (found_key.type != BTRFS_BLOCK_GROUP_ITEM_KEY) {
+ ret = next_block_group(root, path);
+ if (ret < 0)
+ goto out;
+ if (ret > 0) {
+ ret = 0;
+ goto out;
+ }
+ btrfs_item_key_to_cpu(path->nodes[0], &found_key,
+ path->slots[0]);
+ }
+
+ bgi = btrfs_item_ptr(path->nodes[0], path->slots[0],
+ struct btrfs_block_group_item);
+ if (is_temp_block_group(path->nodes[0], bgi,
+ data_profile, meta_profile,
+ sys_profile)) {
+ ret = btrfs_free_block_group(trans, fs_info,
+ found_key.objectid, found_key.offset);
+ if (ret < 0)
+ goto out;
+ }
+ btrfs_release_path(path);
+ key.objectid = found_key.objectid + found_key.offset;
+ }
+out:
+ if (trans)
+ btrfs_commit_transaction(trans, root);
+ btrfs_free_path(path);
+ return ret;
+}
+
int main(int ac, char **av)
{
char *file;
@@ -1321,9 +1466,8 @@ int main(int ac, char **av)
print_usage(c != GETOPT_VAL_HELP);
}
}
+
sectorsize = max(sectorsize, (u32)sysconf(_SC_PAGESIZE));
- if (btrfs_check_nodesize(nodesize, sectorsize))
- exit(1);
saved_optind = optind;
dev_cnt = ac - optind;
if (dev_cnt == 0)
@@ -1350,7 +1494,7 @@ int main(int ac, char **av)
while (dev_cnt-- > 0) {
file = av[optind++];
- if (is_block_device(file))
+ if (is_block_device(file) == 1)
if (test_dev_for_mkfs(file, force_overwrite))
exit(1);
}
@@ -1397,17 +1541,12 @@ int main(int ac, char **av)
}
}
- if (!nodesize_forced) {
+ if (!nodesize_forced)
nodesize = best_nodesize;
- if (btrfs_check_nodesize(nodesize, sectorsize))
- exit(1);
- }
- if (nodesize != sectorsize) {
- fprintf(stderr, "Error: mixed metadata/data block groups "
- "require metadata blocksizes equal to the sectorsize\n");
- exit(1);
- }
}
+ if (btrfs_check_nodesize(nodesize, sectorsize,
+ features))
+ exit(1);
/* Check device/block_count after the nodesize is determined */
if (block_count && block_count < btrfs_min_dev_size(nodesize)) {
@@ -1554,7 +1693,10 @@ int main(int ac, char **av)
}
trans = btrfs_start_transaction(root, 1);
- BUG_ON(!trans);
+ if (!trans) {
+ fprintf(stderr, "failed to start transaction\n");
+ exit(1);
+ }
ret = create_data_block_groups(trans, root, mixed, &allocation);
if (ret) {
@@ -1571,8 +1713,12 @@ int main(int ac, char **av)
btrfs_commit_transaction(trans, root);
trans = btrfs_start_transaction(root, 1);
+ if (!trans) {
+ fprintf(stderr, "failed to start transaction\n");
+ exit(1);
+ }
- if (is_block_device(file))
+ if (is_block_device(file) == 1)
btrfs_register_one_device(file);
if (dev_cnt == 0)
@@ -1622,7 +1768,7 @@ int main(int ac, char **av)
(unsigned long long)device->devid);
}
- if (is_block_device(file))
+ if (is_block_device(file) == 1)
btrfs_register_one_device(file);
}
@@ -1649,6 +1795,12 @@ raid_groups:
ret = make_image(source_dir, root, fd);
BUG_ON(ret);
}
+ ret = cleanup_temp_chunks(root->fs_info, &allocation, data_profile,
+ metadata_profile, metadata_profile);
+ if (ret < 0) {
+ fprintf(stderr, "Failed to cleanup temporary chunks\n");
+ goto out;
+ }
if (verbose) {
char features_buf[64];
@@ -1683,8 +1835,10 @@ raid_groups:
list_all_devices(root);
}
+out:
ret = close_ctree(root);
BUG_ON(ret);
+ btrfs_close_all_devices();
free(label);
return 0;
}
diff --git a/print-tree.c b/print-tree.c
index a72a979..dc1d276 100644
--- a/print-tree.c
+++ b/print-tree.c
@@ -224,9 +224,11 @@ void print_chunk(struct extent_buffer *eb, struct btrfs_chunk *chunk)
char chunk_flags_str[32] = {0};
bg_flags_to_str(btrfs_chunk_type(eb, chunk), chunk_flags_str);
- printf("\t\tchunk length %llu owner %llu type %s num_stripes %d\n",
+ printf("\t\tchunk length %llu owner %llu stripe_len %llu\n",
(unsigned long long)btrfs_chunk_length(eb, chunk),
(unsigned long long)btrfs_chunk_owner(eb, chunk),
+ (unsigned long long)btrfs_chunk_stripe_len(eb, chunk));
+ printf("\t\ttype %s num_stripes %d\n",
chunk_flags_str, num_stripes);
for (i = 0 ; i < num_stripes ; i++) {
printf("\t\t\tstripe %d devid %llu offset %llu\n", i,
diff --git a/qgroup.c b/qgroup.c
index dc04b03..ec9a3ac 100644
--- a/qgroup.c
+++ b/qgroup.c
@@ -272,7 +272,7 @@ static void print_single_qgroup_table(struct btrfs_qgroup *qgroup)
printf("\n");
}
-static void print_table_head()
+static void print_table_head(void)
{
int i;
int len;
@@ -465,12 +465,16 @@ int btrfs_qgroup_setup_comparer(struct btrfs_qgroup_comparer_set **comp_set,
BUG_ON(set->ncomps > set->total);
if (set->ncomps == set->total) {
+ void *tmp;
+
size = set->total + BTRFS_QGROUP_NCOMPS_INCREASE;
size = sizeof(*set) +
size * sizeof(struct btrfs_qgroup_comparer);
+ tmp = set;
set = realloc(set, size);
if (!set) {
fprintf(stderr, "memory allocation failed\n");
+ free(tmp);
exit(1);
}
@@ -836,12 +840,16 @@ int btrfs_qgroup_setup_filter(struct btrfs_qgroup_filter_set **filter_set,
BUG_ON(set->nfilters > set->total);
if (set->nfilters == set->total) {
+ void *tmp;
+
size = set->total + BTRFS_QGROUP_NFILTERS_INCREASE;
size = sizeof(*set) + size * sizeof(struct btrfs_qgroup_filter);
+ tmp = set;
set = realloc(set, size);
if (!set) {
fprintf(stderr, "memory allocation failed\n");
+ free(tmp);
exit(1);
}
memset(&set->filters[set->total], 0,
diff --git a/task-utils.c b/task-utils.c
index 10e3f0f..a4017ff 100644
--- a/task-utils.c
+++ b/task-utils.c
@@ -50,10 +50,8 @@ int task_start(struct task_info *info)
ret = pthread_create(&info->id, NULL, info->threadfn,
info->private_data);
- if (ret == 0)
- pthread_detach(info->id);
- else
- info->id = -1;
+ if (ret)
+ info->id = 0;
return ret;
}
@@ -63,11 +61,16 @@ void task_stop(struct task_info *info)
if (!info)
return;
- if (info->periodic.timer_fd)
- close(info->periodic.timer_fd);
-
- if (info->id > 0)
+ if (info->id > 0) {
pthread_cancel(info->id);
+ pthread_join(info->id, NULL);
+ info->id = 0;
+ }
+
+ if (info->periodic.timer_fd) {
+ close(info->periodic.timer_fd);
+ info->periodic.timer_fd = 0;
+ }
if (info->postfn)
info->postfn(info->private_data);
@@ -91,8 +94,10 @@ int task_period_start(struct task_info *info, unsigned int period_ms)
return -1;
info->periodic.timer_fd = timerfd_create(CLOCK_MONOTONIC, 0);
- if (info->periodic.timer_fd == -1)
+ if (info->periodic.timer_fd == -1) {
+ info->periodic.timer_fd = 0;
return info->periodic.timer_fd;
+ }
info->periodic.wakeups_missed = 0;
@@ -130,5 +135,6 @@ void task_period_stop(struct task_info *info)
if (info->periodic.timer_fd) {
timerfd_settime(info->periodic.timer_fd, 0, NULL, NULL);
close(info->periodic.timer_fd);
+ info->periodic.timer_fd = -1;
}
}
diff --git a/tests/clean-tests.sh b/tests/clean-tests.sh
index c1dc56a..f7fefdd 100755
--- a/tests/clean-tests.sh
+++ b/tests/clean-tests.sh
@@ -1,12 +1,17 @@
#!/bin/sh
# remove all intermediate files from tests
+SCRIPT_DIR=$(dirname $(readlink -f $0))
+TOP=$(readlink -f $SCRIPT_DIR/../)
+source $TOP/tests/common
+
+setup_root_helper
+
if [ "$BUILD_VERBOSE" = 1 ]; then
verbose=-print
fi
-SCRIPT_DIR=$(dirname $(readlink -f $0))
-TOP=$(readlink -f $SCRIPT_DIR/../)
+$SUDO_HELPER umount "$TEST_MNT" &>/dev/null
if ! cd $TOP/tests; then
echo "ERROR: cannot cd to $TOP/tests"
diff --git a/tests/common b/tests/common
index 899ec7b..63b0d9f 100644
--- a/tests/common
+++ b/tests/common
@@ -9,6 +9,12 @@ _fail()
exit 1
}
+# log a message to the results file
+_log()
+{
+ echo "$*" | tee -a $RESULTS
+}
+
_not_run()
{
echo " [NOTRUN] $*"
@@ -29,6 +35,13 @@ run_check_stdout()
"$@" 2>&1 | tee -a $RESULTS || _fail "failed: $@"
}
+# same as run_check but does not fail the test, output is logged
+run_mayfail()
+{
+ echo "############### $@" >> $RESULTS 2>&1
+ "$@" >> $RESULTS 2>&1 || _log "failed (ignored): $@"
+}
+
check_prereq()
{
if ! [ -f $TOP/$1 ]; then
@@ -142,3 +155,52 @@ setup_root_helper()
fi
SUDO_HELPER=root_helper
}
+
+prepare_test_dev()
+{
+ # num[K/M/G/T...]
+ local size="$1"
+
+ [[ "$TEST_DEV" ]] && return
+ [[ "$size" ]] || size='1G'
+
+ echo "\$TEST_DEV not given, use $TOP/test/test.img as fallback" >> \
+ $RESULTS
+ TEST_DEV="$TOP/tests/test.img"
+
+ truncate -s "$size" "$TEST_DEV" || _not_run "create file for loop device failed"
+}
+
+run_check_mount_test_dev()
+{
+ setup_root_helper
+
+ local loop_opt
+ if [[ -b "$TEST_DEV" ]]; then
+ loop_opt=""
+ elif [[ -f "$TEST_DEV" ]]; then
+ loop_opt="-o loop"
+ else
+ _fail "Invalid \$TEST_DEV: $TEST_DEV"
+ fi
+
+ [[ -d "$TEST_MNT" ]] || {
+ _fail "Invalid \$TEST_MNT: $TEST_MNT"
+ }
+
+ run_check $SUDO_HELPER mount $loop_opt "$@" "$TEST_DEV" "$TEST_MNT"
+}
+
+run_check_umount_test_dev()
+{
+ setup_root_helper
+ run_check $SUDO_HELPER umount "$@" "$TEST_DEV"
+}
+
+init_env()
+{
+ TEST_MNT="${TEST_MNT:-$TOP/tests/mnt}"
+ export TEST_MNT
+ mkdir -p "$TEST_MNT" || { echo "Failed mkdir -p $TEST_MNT"; exit 1; }
+}
+init_env
diff --git a/tests/convert-tests.sh b/tests/convert-tests.sh
index 14dde1f..4d99a61 100755
--- a/tests/convert-tests.sh
+++ b/tests/convert-tests.sh
@@ -9,7 +9,6 @@ unset LANG
LANG=C
SCRIPT_DIR=$(dirname $(readlink -f $0))
TOP=$(readlink -f $SCRIPT_DIR/../)
-TEST_MNT=${TEST_MNT:-$TOP/tests/mnt}
RESULTS="$TOP/tests/convert-tests-results.txt"
IMAGE="$TOP/tests/test.img"
@@ -43,7 +42,7 @@ convert_test() {
# create a file to check btrfs-convert can convert regular file
# correct
- run_check $SUDO_HELPER mount $IMAGE $TEST_MNT
+ run_check $SUDO_HELPER mount -o loop $IMAGE $TEST_MNT
run_check $SUDO_HELPER dd if=/dev/zero of=$TEST_MNT/test bs=$nodesize \
count=1 1>/dev/null 2>&1
run_check $SUDO_HELPER umount $TEST_MNT
@@ -52,6 +51,11 @@ convert_test() {
run_check $TOP/btrfs-show-super $IMAGE
}
+if ! [ -z "$TEST" ]; then
+ echo " [TEST] skipped all convert tests, TEST=$TEST"
+ exit 0
+fi
+
for feature in '' 'extref' 'skinny-metadata' 'no-holes'; do
convert_test "$feature" "ext2 4k nodesize" 4096 mke2fs -b 4096
convert_test "$feature" "ext3 4k nodesize" 4096 mke2fs -j -b 4096
diff --git a/tests/fsck-tests.sh b/tests/fsck-tests.sh
index b0ded6a..b910e85 100755
--- a/tests/fsck-tests.sh
+++ b/tests/fsck-tests.sh
@@ -11,7 +11,6 @@ LANG=C
SCRIPT_DIR=$(dirname $(readlink -f $0))
TOP=$(readlink -f $SCRIPT_DIR/../)
TEST_DEV=${TEST_DEV:-}
-TEST_MNT=${TEST_MNT:-$TOP/tests/mnt}
RESULTS="$TOP/tests/fsck-tests-results.txt"
source $TOP/tests/common
@@ -20,17 +19,35 @@ source $TOP/tests/common
export TOP
export RESULTS
# For custom script needs to verfiy recovery
-export TEST_MNT
export LANG
rm -f $RESULTS
-mkdir -p $TEST_MNT || _fail "unable to create mount point on $TEST_MNT"
# test rely on corrupting blocks tool
check_prereq btrfs-corrupt-block
check_prereq btrfs-image
check_prereq btrfs
+run_one_test() {
+ local testname
+
+ testname="$1"
+ echo " [TEST] $(basename $testname)"
+ cd $testname
+ echo "=== Entering $testname" >> $RESULTS
+ if [ -x test.sh ]; then
+ # Type 2
+ ./test.sh
+ if [ $? -ne 0 ]; then
+ _fail "test failed for case $(basename $testname)"
+ fi
+ else
+ # Type 1
+ check_all_images `pwd`
+ fi
+ cd $TOP
+}
+
# Each dir contains one type of error for btrfsck test.
# Each dir must be one of the following 2 types:
# 1) Only btrfs-image dump
@@ -44,20 +61,8 @@ check_prereq btrfs
# This is for case btrfs-image can't dump or case needs extra
# check/verify
-for i in $(find $TOP/tests/fsck-tests -maxdepth 1 -mindepth 1 -type d | sort)
+for i in $(find $TOP/tests/fsck-tests -maxdepth 1 -mindepth 1 -type d \
+ ${TEST:+-name "$TEST"} | sort)
do
- echo " [TEST] $(basename $i)"
- cd $i
- echo "=== Entering $i" >> $RESULTS
- if [ -x test.sh ]; then
- # Type 2
- ./test.sh
- if [ $? -ne 0 ]; then
- _fail "test failed for case $(basename $i)"
- fi
- else
- # Type 1
- check_all_images `pwd`
- fi
- cd $TOP
+ run_one_test "$i"
done
diff --git a/tests/fsck-tests/006-bad-root-items/test.sh b/tests/fsck-tests/006-bad-root-items/test.sh
index bfbfcfc..421e225 100755
--- a/tests/fsck-tests/006-bad-root-items/test.sh
+++ b/tests/fsck-tests/006-bad-root-items/test.sh
@@ -3,12 +3,12 @@
source $TOP/tests/common
echo "extracting image default_case.tar.xz" >> $RESULTS
-tar xJf default_case.tar.xz || \
+tar --no-same-owner -xJf default_case.tar.xz || \
_fail "failed to extract default_case.tar.xz"
check_image test.img
echo "extracting image skinny_case.tar.xz" >> $RESULTS
-tar xJf skinny_case.tar.xz || \
+tar --no-same-owner -xJf skinny_case.tar.xz || \
_fail "failed to extract skinny_case.tar.xz"
check_image test.img
diff --git a/tests/fsck-tests/012-leaf-corruption/test.sh b/tests/fsck-tests/012-leaf-corruption/test.sh
index 98e3185..6e23145 100755
--- a/tests/fsck-tests/012-leaf-corruption/test.sh
+++ b/tests/fsck-tests/012-leaf-corruption/test.sh
@@ -35,7 +35,7 @@ generate_leaf_corrupt_no_data_ext()
{
dest=$1
echo "generating leaf_corrupt_no_data_ext.btrfs-image" >> $RESULTS
- tar xJf ./no_data_extent.tar.xz || \
+ tar --no-same-owner -xJf ./no_data_extent.tar.xz || \
_fail "failed to extract leaf_corrupt_no_data_ext.btrfs-image"
$TOP/btrfs-image -r test.img.btrfs-image $dest || \
_fail "failed to extract leaf_corrupt_no_data_ext.btrfs-image"
@@ -85,8 +85,7 @@ check_inode()
check_leaf_corrupt_no_data_ext()
{
image=$1
- mkdir -p $TEST_MNT || _fail "failed to create mount point"
- $SUDO_HELPER mount $image -o ro $TEST_MNT
+ $SUDO_HELPER mount -o loop $image -o ro $TEST_MNT
i=0
while [ $i -lt ${#leaf_no_data_ext_list[@]} ]; do
diff --git a/tests/fsck-tests/013-extent-tree-rebuild/test.sh b/tests/fsck-tests/013-extent-tree-rebuild/test.sh
index 3290cd7..7419d6e 100755
--- a/tests/fsck-tests/013-extent-tree-rebuild/test.sh
+++ b/tests/fsck-tests/013-extent-tree-rebuild/test.sh
@@ -5,35 +5,21 @@ source $TOP/tests/common
check_prereq btrfs-debug-tree
check_prereq mkfs.btrfs
setup_root_helper
-
-if [ -z $TEST_DEV ]; then
- echo "\$TEST_DEV not given, use $TOP/test/test.img as fallback" >> \
- $RESULTS
- TEST_DEV="$TOP/tests/test.img"
-
- # Need at least 1G to avoid mixed block group, which extent tree
- # rebuild doesn't support.
- run_check truncate -s 1G $TEST_DEV
-fi
-
-if [ -z $TEST_MNT ];then
- echo " [NOTRUN] extent tree rebuild, need TEST_MNT variant"
- exit 0
-fi
+prepare_test_dev 1G
# test whether fsck can rebuild a corrupted extent tree
test_extent_tree_rebuild()
{
run_check $SUDO_HELPER $TOP/mkfs.btrfs -f $TEST_DEV
- run_check $SUDO_HELPER mount $TEST_DEV $TEST_MNT
+ run_check_mount_test_dev
run_check $SUDO_HELPER cp -aR /lib/modules/`uname -r`/ $TEST_MNT
for i in `seq 1 100`;do
run_check $SUDO_HELPER $TOP/btrfs sub snapshot $TEST_MNT \
$TEST_MNT/snapaaaaaaa_$i
done
- run_check $SUDO_HELPER umount $TEST_DEV
+ run_check_umount_test_dev
# get extent root bytenr
extent_root_bytenr=`$SUDO_HELPER $TOP/btrfs-debug-tree -r $TEST_DEV | \
diff --git a/tests/fsck-tests/017-missing-all-file-extent/default_case.img.xz b/tests/fsck-tests/017-missing-all-file-extent/default_case.img.xz
new file mode 100644
index 0000000..10cd4c7
--- /dev/null
+++ b/tests/fsck-tests/017-missing-all-file-extent/default_case.img.xz
Binary files differ
diff --git a/tests/fuzz-tests/images/bad-superblock-1.raw.xz b/tests/fuzz-tests/images/bad-superblock-1.raw.xz
new file mode 100644
index 0000000..3d6358f
--- /dev/null
+++ b/tests/fuzz-tests/images/bad-superblock-1.raw.xz
Binary files differ
diff --git a/tests/fuzz-tests/images/bad-superblock-2.raw.xz b/tests/fuzz-tests/images/bad-superblock-2.raw.xz
new file mode 100644
index 0000000..7db7610
--- /dev/null
+++ b/tests/fuzz-tests/images/bad-superblock-2.raw.xz
Binary files differ
diff --git a/tests/fuzz-tests/images/bad-superblock-3.raw.xz b/tests/fuzz-tests/images/bad-superblock-3.raw.xz
new file mode 100644
index 0000000..4aa3148
--- /dev/null
+++ b/tests/fuzz-tests/images/bad-superblock-3.raw.xz
Binary files differ
diff --git a/tests/fuzz-tests/images/bad-superblock.txt b/tests/fuzz-tests/images/bad-superblock.txt
new file mode 100644
index 0000000..f7dd9aa
--- /dev/null
+++ b/tests/fuzz-tests/images/bad-superblock.txt
@@ -0,0 +1,17 @@
+bad-superblock-*.txt
+
+Crafted images from Jiri Slaby, produced by some symbolic execution framework
+that finds unhandled cases at mount time.
+
+Relevant kernel patches to backport:
+
+e3540eab29e1b2260bc4b9b3979a49a00e3e3af8
+btrfs: add more checks to btrfs_read_sys_array
+
+ce7fca5f57ed0fcd7e7b3d7b1a3e1791f8e56fa3
+btrfs: add checks for sys_chunk_array sizes
+
+75d6ad382bb91f363452119d34238e156589ca2d
+btrfs: more superblock checks, lower bounds on devices and sectorsize/nodesize
+
+(and more from fs/btrfs/super.c)
diff --git a/tests/fuzz-tests/images/bko-104131-fsck-oob-read.raw.xz b/tests/fuzz-tests/images/bko-104131-fsck-oob-read.raw.xz
new file mode 100644
index 0000000..7848f8b
--- /dev/null
+++ b/tests/fuzz-tests/images/bko-104131-fsck-oob-read.raw.xz
Binary files differ
diff --git a/tests/fuzz-tests/images/bko-104131-fsck-oob-read.txt b/tests/fuzz-tests/images/bko-104131-fsck-oob-read.txt
new file mode 100644
index 0000000..0e829c2
--- /dev/null
+++ b/tests/fuzz-tests/images/bko-104131-fsck-oob-read.txt
@@ -0,0 +1,31 @@
+URL: https://bugzilla.kernel.org/show_bug.cgi?id=104131
+Hanno Boeck 2015-09-07 07:24:32 UTC
+
+Created attachment 186941 [details]
+malformed btrfs filesystem causing oob read
+
+The attached malformed filesystem image will cause an invalid heap out of bounds memory read in btrfsck.
+
+This was found while fuzzing btrfs-progs with american fuzzy lop.
+
+Stack trace from Address Sanitizer:
+==31289==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60f00000f003 at pc 0x0000005d0dbb bp 0x7ffdf444c180 sp 0x7ffdf444c178
+READ of size 8 at 0x60f00000f003 thread T0
+ #0 0x5d0dba in btrfs_header_bytenr /mnt/ram/btrfs-progs-v4.1.2/./ctree.h:1797:1
+ #1 0x5d0dba in check_tree_block /mnt/ram/btrfs-progs-v4.1.2/disk-io.c:60
+ #2 0x5d0dba in read_tree_block /mnt/ram/btrfs-progs-v4.1.2/disk-io.c:337
+ #3 0x5dc00e in btrfs_setup_chunk_tree_and_device_map /mnt/ram/btrfs-progs-v4.1.2/disk-io.c:1169:30
+ #4 0x5dcf89 in __open_ctree_fd /mnt/ram/btrfs-progs-v4.1.2/disk-io.c:1261:8
+ #5 0x5dc50a in open_ctree_fs_info /mnt/ram/btrfs-progs-v4.1.2/disk-io.c:1302:9
+ #6 0x52f22f in cmd_check /mnt/ram/btrfs-progs-v4.1.2/cmds-check.c:9333:9
+ #7 0x4e7bcc in main /mnt/ram/btrfs-progs-v4.1.2/btrfs.c:245:7
+ #8 0x7f98bb101f9f in __libc_start_main /var/tmp/portage/sys-libs/glibc-2.20-r2/work/glibc-2.20/csu/libc-start.c:289
+ #9 0x41f748 in _start (/mnt/ram/btrfs/btrfs+0x41f748)
+
+0x60f00000f003 is located 3 bytes to the right of 176-byte region [0x60f00000ef50,0x60f00000f000)
+allocated by thread T0 here:
+ #0 0x4bade8 in malloc (/mnt/ram/btrfs/btrfs+0x4bade8)
+ #1 0x622c24 in __alloc_extent_buffer /mnt/ram/btrfs-progs-v4.1.2/extent_io.c:541:7
+ #2 0x622c24 in alloc_extent_buffer /mnt/ram/btrfs-progs-v4.1.2/extent_io.c:648
+ #3 0x5cf436 in btrfs_find_create_tree_block /mnt/ram/btrfs-progs-v4.1.2/disk-io.c:186:9
+ #4 0x5cf436 in read_tree_block /mnt/ram/btrfs-progs-v4.1.2/disk-io.c:314
diff --git a/tests/fuzz-tests/images/bko-104141-fsck-exception.raw.xz b/tests/fuzz-tests/images/bko-104141-fsck-exception.raw.xz
new file mode 100644
index 0000000..d24a32f
--- /dev/null
+++ b/tests/fuzz-tests/images/bko-104141-fsck-exception.raw.xz
Binary files differ
diff --git a/tests/fuzz-tests/images/bko-104141-fsck-exception.txt b/tests/fuzz-tests/images/bko-104141-fsck-exception.txt
new file mode 100644
index 0000000..aed9190
--- /dev/null
+++ b/tests/fuzz-tests/images/bko-104141-fsck-exception.txt
@@ -0,0 +1,9 @@
+URL: https://bugzilla.kernel.org/show_bug.cgi?id=104141
+Hanno Boeck 2015-09-07 07:27:58 UTC
+
+Created attachment 186951 [details]
+malformed filesystem causing floating point exception
+
+The attacked file will cause a floating point exception in btrfsck.
+
+This was found while fuzzing btrfs-progs with american fuzzy lop.
diff --git a/tests/fuzz-tests/images/bko-97191-btrfs-image.raw.txt b/tests/fuzz-tests/images/bko-97191-btrfs-image.raw.txt
new file mode 100644
index 0000000..f0d8189
--- /dev/null
+++ b/tests/fuzz-tests/images/bko-97191-btrfs-image.raw.txt
@@ -0,0 +1,137 @@
+URL: https://bugzilla.kernel.org/show_bug.cgi?id=97191
+Lukas Lueg 2015-04-23 22:20:35 UTC
+
+Running btrfs-progs v3.19.1
+
+The btrfs-image attached to this bug causes the btrfs-userland tool to
+overflow some data structures, leading to unallocated memory being written to
+and read from. A segfault results shortly after. Reproduced on x86-64 and
+i686.
+
+The kernel seems to be less affected and fails to mount the image. I didn't
+investigate whether the reads/writes could be used to gain control over $EIP.
+Since the first invalid write of 8 bytes seems to run into adjacent heap
+blocks (crash in unlink()), it may be possible though.
+
+gdb output:
+
+Program received signal SIGSEGV, Segmentation fault.
+malloc_consolidate (av=av@entry=0x32629b7cc0 <main_arena>) at malloc.c:4151
+4151 unlink(av, p, bck, fwd);
+(gdb) bt
+#0 malloc_consolidate (av=av@entry=0x32629b7cc0 <main_arena>) at malloc.c:4151
+#1 0x0000003262680628 in _int_malloc (av=av@entry=0x32629b7cc0 <main_arena>, bytes=bytes@entry=4224) at malloc.c:3420
+#2 0x000000326268315e in __GI___libc_malloc (bytes=4224) at malloc.c:2896
+#3 0x0000000000449d15 in __alloc_extent_buffer (tree=0x88c078, bytenr=4288512, blocksize=4096) at extent_io.c:541
+#4 0x000000000044a8b4 in alloc_extent_buffer (tree=0x88c078, bytenr=4288512, blocksize=4096) at extent_io.c:648
+#5 0x000000000043b1a0 in btrfs_find_create_tree_block (root=root@entry=0x895840, bytenr=<optimized out>,
+ blocksize=<optimized out>) at disk-io.c:159
+#6 0x000000000043ca4e in read_tree_block (root=root@entry=0x895840, bytenr=<optimized out>, blocksize=<optimized out>,
+ parent_transid=13) at disk-io.c:287
+#7 0x000000000043ccb7 in find_and_setup_root (tree_root=0x88c250, fs_info=<optimized out>, objectid=5, root=0x895840)
+ at disk-io.c:557
+#8 0x000000000043ce92 in btrfs_read_fs_root_no_cache (fs_info=fs_info@entry=0x88c010, location=location@entry=0x7fffffffd960)
+ at disk-io.c:640
+#9 0x000000000043d060 in btrfs_read_fs_root (fs_info=fs_info@entry=0x88c010, location=location@entry=0x7fffffffd960)
+ at disk-io.c:739
+#10 0x000000000043d48c in btrfs_setup_all_roots (fs_info=fs_info@entry=0x88c010, root_tree_bytenr=<optimized out>,
+ root_tree_bytenr@entry=0, flags=flags@entry=OPEN_CTREE_EXCLUSIVE) at disk-io.c:988
+#11 0x000000000043d802 in __open_ctree_fd (fp=fp@entry=3, path=path@entry=0x7fffffffe20d "ramdisk/btrfs_fukked.bin",
+ sb_bytenr=65536, sb_bytenr@entry=0, root_tree_bytenr=root_tree_bytenr@entry=0, flags=flags@entry=OPEN_CTREE_EXCLUSIVE)
+ at disk-io.c:1199
+#12 0x000000000043d965 in open_ctree_fs_info (filename=0x7fffffffe20d "ramdisk/btrfs_fukked.bin", sb_bytenr=sb_bytenr@entry=0,
+ root_tree_bytenr=root_tree_bytenr@entry=0, flags=flags@entry=OPEN_CTREE_EXCLUSIVE) at disk-io.c:1231
+#13 0x0000000000427bf5 in cmd_check (argc=1, argv=0x7fffffffdea0) at cmds-check.c:9326
+#14 0x000000000040e5a2 in main (argc=2, argv=0x7fffffffdea0) at btrfs.c:245
+
+
+valgrind output:
+
+==32463== Memcheck, a memory error detector
+==32463== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
+==32463== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
+==32463== Command: btrfs check ramdisk/btrfs_fukked.bin
+==32463==
+==32463== Invalid write of size 8
+==32463== at 0x4386FB: btrfs_search_slot (ctree.c:1119)
+==32463== by 0x4427F7: UnknownInlinedFun (extent-tree.c:3117)
+==32463== by 0x4427F7: btrfs_read_block_groups (extent-tree.c:3167)
+==32463== by 0x43D4F2: btrfs_setup_all_roots (disk-io.c:983)
+==32463== by 0x43D801: __open_ctree_fd (disk-io.c:1199)
+==32463== by 0x43D964: open_ctree_fs_info (disk-io.c:1231)
+==32463== by 0x427BF4: cmd_check (cmds-check.c:9326)
+==32463== by 0x40E5A1: main (btrfs.c:245)
+==32463== Address 0x4c409f0 is 16 bytes after a block of size 144 alloc'd
+==32463== at 0x4A08946: calloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
+==32463== by 0x4427AB: btrfs_read_block_groups (extent-tree.c:3162)
+==32463== by 0x43D4F2: btrfs_setup_all_roots (disk-io.c:983)
+==32463== by 0x43D801: __open_ctree_fd (disk-io.c:1199)
+==32463== by 0x43D964: open_ctree_fs_info (disk-io.c:1231)
+==32463== by 0x427BF4: cmd_check (cmds-check.c:9326)
+==32463== by 0x40E5A1: main (btrfs.c:245)
+==32463==
+==32463== Invalid read of size 8
+==32463== at 0x436E70: check_block.part.14 (ctree.c:548)
+==32463== by 0x438954: UnknownInlinedFun (kerncompat.h:91)
+==32463== by 0x438954: btrfs_search_slot (ctree.c:1120)
+==32463== by 0x4427F7: UnknownInlinedFun (extent-tree.c:3117)
+==32463== by 0x4427F7: btrfs_read_block_groups (extent-tree.c:3167)
+==32463== by 0x43D4F2: btrfs_setup_all_roots (disk-io.c:983)
+==32463== by 0x43D801: __open_ctree_fd (disk-io.c:1199)
+==32463== by 0x43D964: open_ctree_fs_info (disk-io.c:1231)
+==32463== by 0x427BF4: cmd_check (cmds-check.c:9326)
+==32463== by 0x40E5A1: main (btrfs.c:245)
+==32463== Address 0x4c409f8 is 24 bytes after a block of size 144 in arena "client"
+==32463==
+==32463== Invalid read of size 4
+==32463== at 0x436E84: UnknownInlinedFun (ctree.h:1605)
+==32463== by 0x436E84: UnknownInlinedFun (ctree.h:1612)
+==32463== by 0x436E84: check_block.part.14 (ctree.c:550)
+==32463== by 0x438954: UnknownInlinedFun (kerncompat.h:91)
+==32463== by 0x438954: btrfs_search_slot (ctree.c:1120)
+==32463== by 0x4427F7: UnknownInlinedFun (extent-tree.c:3117)
+==32463== by 0x4427F7: btrfs_read_block_groups (extent-tree.c:3167)
+==32463== by 0x43D4F2: btrfs_setup_all_roots (disk-io.c:983)
+==32463== by 0x43D801: __open_ctree_fd (disk-io.c:1199)
+==32463== by 0x43D964: open_ctree_fs_info (disk-io.c:1231)
+==32463== by 0x427BF4: cmd_check (cmds-check.c:9326)
+==32463== by 0x40E5A1: main (btrfs.c:245)
+==32463== Address 0x4c409e4 is 4 bytes after a block of size 144 alloc'd
+==32463== at 0x4A08946: calloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
+==32463== by 0x4427AB: btrfs_read_block_groups (extent-tree.c:3162)
+==32463== by 0x43D4F2: btrfs_setup_all_roots (disk-io.c:983)
+==32463== by 0x43D801: __open_ctree_fd (disk-io.c:1199)
+==32463== by 0x43D964: open_ctree_fs_info (disk-io.c:1231)
+==32463== by 0x427BF4: cmd_check (cmds-check.c:9326)
+==32463== by 0x40E5A1: main (btrfs.c:245)
+==32463==
+==32463== Invalid read of size 1
+==32463== at 0x4A0B3A0: memcpy@@GLIBC_2.14 (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
+==32463== by 0x436E99: UnknownInlinedFun (ctree.h:1613)
+==32463== by 0x436E99: check_block.part.14 (ctree.c:550)
+==32463== by 0x438954: UnknownInlinedFun (kerncompat.h:91)
+==32463== by 0x438954: btrfs_search_slot (ctree.c:1120)
+==32463== by 0x4427F7: UnknownInlinedFun (extent-tree.c:3117)
+==32463== by 0x4427F7: btrfs_read_block_groups (extent-tree.c:3167)
+==32463== by 0x43D4F2: btrfs_setup_all_roots (disk-io.c:983)
+==32463== by 0x43D801: __open_ctree_fd (disk-io.c:1199)
+==32463== by 0x43D964: open_ctree_fs_info (disk-io.c:1231)
+==32463== by 0x427BF4: cmd_check (cmds-check.c:9326)
+==32463== by 0x40E5A1: main (btrfs.c:245)
+==32463== Address 0x1b1 is not stack'd, malloc'd or (recently) free'd
+==32463==
+==32463==
+==32463== Process terminating with default action of signal 11 (SIGSEGV)
+==32463== Access not within mapped region at address 0x1B1
+==32463== at 0x4A0B3A0: memcpy@@GLIBC_2.14 (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
+==32463== by 0x436E99: UnknownInlinedFun (ctree.h:1613)
+==32463== by 0x436E99: check_block.part.14 (ctree.c:550)
+==32463== by 0x438954: UnknownInlinedFun (kerncompat.h:91)
+==32463== by 0x438954: btrfs_search_slot (ctree.c:1120)
+==32463== by 0x4427F7: UnknownInlinedFun (extent-tree.c:3117)
+==32463== by 0x4427F7: btrfs_read_block_groups (extent-tree.c:3167)
+==32463== by 0x43D4F2: btrfs_setup_all_roots (disk-io.c:983)
+==32463== by 0x43D801: __open_ctree_fd (disk-io.c:1199)
+==32463== by 0x43D964: open_ctree_fs_info (disk-io.c:1231)
+==32463== by 0x427BF4: cmd_check (cmds-check.c:9326)
+==32463== by 0x40E5A1: main (btrfs.c:245)
diff --git a/tests/fuzz-tests/images/bko-97191-btrfs-image.raw.xz b/tests/fuzz-tests/images/bko-97191-btrfs-image.raw.xz
new file mode 100644
index 0000000..b2e48c0
--- /dev/null
+++ b/tests/fuzz-tests/images/bko-97191-btrfs-image.raw.xz
Binary files differ
diff --git a/tests/fuzz-tests/images/bko-97271-btrfs-image.raw.txt b/tests/fuzz-tests/images/bko-97271-btrfs-image.raw.txt
new file mode 100644
index 0000000..67f2096
--- /dev/null
+++ b/tests/fuzz-tests/images/bko-97271-btrfs-image.raw.txt
@@ -0,0 +1,54 @@
+URL: https://bugzilla.kernel.org/show_bug.cgi?id=97271
+Lukas Lueg 2015-04-25 20:34:39 UTC
+
+The attached btrfs-image causes "btrfs check" to write outside of allocated
+memory locations and ultimately die due to a segfault. An adjacent heap block's
+control structure is overwritten with a `struct extent_buffer *`, which is not
+controllable by the user.
+
+"btrfs version" is v3.19.1. Running "btrfs check" immediately dies with
+
+*** Error in `btrfs': double free or corruption (!prev): 0x0000000002396ec0 ***
+*** Error in `btrfs': malloc(): memory corruption: 0x0000000002396f60 ***
+
+Debugging with valgrind and gdb gives
+
+==11670== Invalid write of size 8
+==11670== at 0x4386FB: btrfs_search_slot (ctree.c:1119)
+==11670== by 0x44E16E: btrfs_read_chunk_tree (volumes.c:1814)
+==11670== by 0x43D654: btrfs_setup_chunk_tree_and_device_map (disk-io.c:1115)
+==11670== by 0x43D7D0: __open_ctree_fd (disk-io.c:1190)
+==11670== by 0x43D964: open_ctree_fs_info (disk-io.c:1231)
+==11670== by 0x427BF4: cmd_check (cmds-check.c:9326)
+==11670== by 0x40E5A1: main (btrfs.c:245)
+==11670== Address 0x4c3bb98 is 8 bytes after a block of size 144 alloc'd
+==11670== at 0x4A08946: calloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
+==11670== by 0x44E133: btrfs_read_chunk_tree (volumes.c:1801)
+==11670== by 0x43D654: btrfs_setup_chunk_tree_and_device_map (disk-io.c:1115)
+==11670== by 0x43D7D0: __open_ctree_fd (disk-io.c:1190)
+==11670== by 0x43D964: open_ctree_fs_info (disk-io.c:1231)
+==11670== by 0x427BF4: cmd_check (cmds-check.c:9326)
+==11670== by 0x40E5A1: main (btrfs.c:245)
+
+Program received signal SIGTRAP, Trace/breakpoint trap.
+btrfs_search_slot (trans=trans@entry=0x0, root=root@entry=0x4c36d30, key=key@entry=0xffefff830, p=p@entry=0x4c3bb00,
+ ins_len=ins_len@entry=0, cow=cow@entry=0) at ctree.c:1119
+1119 p->nodes[level] = b;
+(gdb) p p->nodes
+$1 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}
+(gdb) p p
+$2 = (struct btrfs_path *) 0x4c3bb00
+(gdb) p b
+$3 = (struct extent_buffer *) 0x4c3a990
+
+
+The corresponding part in ctree.c:btrfs_search_slot() seems to fail to check if `level` overflows outside of `node`:
+
+level = btrfs_header_level(b);
+...
+if (level != btrfs_header_level(b))
+ WARN_ON(1);
+level = btrfs_header_level(b);
+p->nodes[level] = b; // <- Illegal write
+
+Maybe the repeated calls to btrfs_header_level() were meant to do something once, they seem to be noise.
diff --git a/tests/fuzz-tests/images/bko-97271-btrfs-image.raw.xz b/tests/fuzz-tests/images/bko-97271-btrfs-image.raw.xz
new file mode 100644
index 0000000..3c79edb
--- /dev/null
+++ b/tests/fuzz-tests/images/bko-97271-btrfs-image.raw.xz
Binary files differ
diff --git a/tests/misc-tests.sh b/tests/misc-tests.sh
index 5bbe914..a87ece2 100755
--- a/tests/misc-tests.sh
+++ b/tests/misc-tests.sh
@@ -8,7 +8,6 @@ LANG=C
SCRIPT_DIR=$(dirname $(readlink -f $0))
TOP=$(readlink -f $SCRIPT_DIR/../)
TEST_DEV=${TEST_DEV:-}
-TEST_MNT=${TEST_MNT:-$TOP/tests/mnt}
RESULTS="$TOP/tests/misc-tests-results.txt"
IMAGE="$TOP/tests/test.img"
@@ -18,11 +17,11 @@ source $TOP/tests/common
export TOP
export RESULTS
# For custom script needs to verfiy recovery
-export TEST_MNT
export LANG
+# For tests that only use a loop device
+export IMAGE
rm -f $RESULTS
-mkdir -p $TEST_MNT || _fail "unable to create mount point on $TEST_MNT"
# test rely on corrupting blocks tool
check_prereq btrfs-corrupt-block
@@ -32,7 +31,8 @@ check_prereq btrfs
# The tests are driven by their custom script called 'test.sh'
-for i in $(find $TOP/tests/misc-tests -maxdepth 1 -mindepth 1 -type d | sort)
+for i in $(find $TOP/tests/misc-tests -maxdepth 1 -mindepth 1 -type d \
+ ${TEST:+-name "$TEST"} | sort)
do
echo " [TEST] $(basename $i)"
cd $i
diff --git a/tests/misc-tests/001-btrfstune-features/test.sh b/tests/misc-tests/001-btrfstune-features/test.sh
index c9981e6..836e8d3 100755
--- a/tests/misc-tests/001-btrfstune-features/test.sh
+++ b/tests/misc-tests/001-btrfstune-features/test.sh
@@ -7,21 +7,7 @@ check_prereq btrfs-debug-tree
check_prereq btrfs-show-super
check_prereq mkfs.btrfs
setup_root_helper
-
-if [ -z $TEST_DEV ]; then
- echo "\$TEST_DEV not given, use $TOP/test/test.img as fallback" >> \
- $RESULTS
- TEST_DEV="$TOP/tests/test.img"
-
- # Need at least 1G to avoid mixed block group, which extent tree
- # rebuild doesn't support.
- run_check truncate -s 1G $TEST_DEV
-fi
-
-if [ -z $TEST_MNT ];then
- echo " [NOTRUN] extent tree rebuild, need TEST_MNT variant"
- exit 0
-fi
+prepare_test_dev
# test whether fsck can rebuild a corrupted extent tree
# parameters:
diff --git a/tests/misc-tests/002-uuid-rewrite/test.sh b/tests/misc-tests/002-uuid-rewrite/test.sh
index 6c2a393..9b103aa 100755
--- a/tests/misc-tests/002-uuid-rewrite/test.sh
+++ b/tests/misc-tests/002-uuid-rewrite/test.sh
@@ -7,21 +7,7 @@ check_prereq btrfs-debug-tree
check_prereq btrfs-show-super
check_prereq mkfs.btrfs
check_prereq btrfstune
-
-if [ -z $TEST_DEV ]; then
- echo "\$TEST_DEV not given, use $TOP/test/test.img as fallback" >> \
- $RESULTS
- TEST_DEV="$TOP/tests/test.img"
-
- # Need at least 1G to avoid mixed block group, which extent tree
- # rebuild doesn't support.
- run_check truncate -s 1G $TEST_DEV
-fi
-
-if [ -z $TEST_MNT ];then
- echo " [NOTRUN] extent tree rebuild, need TEST_MNT variant"
- exit 0
-fi
+prepare_test_dev
get_fs_uuid() {
local image
diff --git a/tests/misc-tests/003-zero-log/test.sh b/tests/misc-tests/003-zero-log/test.sh
index da5b351..b650930 100755
--- a/tests/misc-tests/003-zero-log/test.sh
+++ b/tests/misc-tests/003-zero-log/test.sh
@@ -6,21 +6,7 @@ source $TOP/tests/common
check_prereq btrfs-show-super
check_prereq mkfs.btrfs
check_prereq btrfs
-
-if [ -z $TEST_DEV ]; then
- echo "\$TEST_DEV not given, use $TOP/test/test.img as fallback" >> \
- $RESULTS
- TEST_DEV="$TOP/tests/test.img"
-
- # Need at least 1G to avoid mixed block group, which extent tree
- # rebuild doesn't support.
- run_check truncate -s 1G $TEST_DEV
-fi
-
-if [ -z $TEST_MNT ];then
- echo " [NOTRUN] extent tree rebuild, need TEST_MNT variant"
- exit 0
-fi
+prepare_test_dev
get_log_root()
{
diff --git a/tests/misc-tests/004-shrink-fs/test.sh b/tests/misc-tests/004-shrink-fs/test.sh
new file mode 100755
index 0000000..b132152
--- /dev/null
+++ b/tests/misc-tests/004-shrink-fs/test.sh
@@ -0,0 +1,70 @@
+#!/bin/bash
+#
+# Test getting the minimum size a filesystem can be resized to and verify we
+# are able to resize (shrink) it to that size.
+#
+
+source $TOP/tests/common
+
+check_prereq mkfs.btrfs
+setup_root_helper
+
+# Optionally take id of the device to shrink
+shrink_test()
+{
+ min_size=$(run_check_stdout $SUDO_HELPER $TOP/btrfs inspect-internal min-dev-size ${1:+--id $1} $TEST_MNT)
+ min_size=$(echo $min_size | cut -d ' ' -f 1)
+ echo "min size = ${min_size}" >> $RESULTS
+ if [ -z "$min_size" ]; then
+ _fail "Failed to parse minimum size"
+ fi
+ run_check $SUDO_HELPER $TOP/btrfs filesystem resize $min_size $TEST_MNT
+}
+
+run_check truncate -s 20G $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
+
+# Create 7 data block groups, each with a size of 1Gb.
+for ((i = 1; i <= 7; i++)); do
+ run_check fallocate -l 1G $TEST_MNT/foo$i
+done
+
+# Make sure they are persisted (all the chunk, device and block group items
+# added to the chunk/dev/extent trees).
+run_check $TOP/btrfs filesystem sync $TEST_MNT
+
+# Now remove 3 of those 1G files. This will result in 3 block groups becoming
+# unused, which will be automatically deleted by the cleaner kthread, and this
+# will result in 3 holes (unallocated space) in the device (each with a size
+# of 1Gb).
+
+run_check rm -f $TEST_MNT/foo2
+run_check rm -f $TEST_MNT/foo4
+run_check rm -f $TEST_MNT/foo6
+
+# Sync once to wake up the cleaner kthread which will delete the unused block
+# groups - it could have been sleeping when they became unused. Then wait a bit
+# to allow the cleaner kthread to delete them and then finally ensure the
+# transaction started by the cleaner kthread is committed.
+run_check $TOP/btrfs filesystem sync $TEST_MNT
+sleep 3
+run_check $TOP/btrfs filesystem sync $TEST_MNT
+
+# Now attempt to get the minimum size we can resize the filesystem to and verify
+# the resize operation succeeds. This size closely matches the sum of the size
+# of all the allocated device extents.
+for ((i = 1; i <= 3; i++)); do
+ shrink_test
+done
+
+# Now convert metadata and system chunks to the single profile and check we are
+# still able to get a correct minimum size and shrink to that size.
+run_check $SUDO_HELPER $TOP/btrfs balance start -mconvert=single \
+ -sconvert=single -f $TEST_MNT
+for ((i = 1; i <= 3; i++)); do
+ shrink_test 1
+done
+
+run_check $SUDO_HELPER umount $TEST_MNT
diff --git a/tests/misc-tests/005-convert-progress-thread-crash/test.sh b/tests/misc-tests/005-convert-progress-thread-crash/test.sh
new file mode 100755
index 0000000..09ac8a3
--- /dev/null
+++ b/tests/misc-tests/005-convert-progress-thread-crash/test.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+# test convert-thread-conflict
+
+source $TOP/tests/common
+
+check_prereq btrfs
+mkfs.ext4 -V &>/dev/null || _not_run "mkfs.ext4 not found"
+prepare_test_dev 1G
+
+for ((i = 0; i < 20; i++)); do
+ echo "loop $i" >>$RESULTS
+ mkfs.ext4 -F "$TEST_DEV" &>>$RESULTS || _not_run "mkfs.ext4 failed"
+ run_check $TOP/btrfs-convert "$TEST_DEV"
+done
diff --git a/tests/misc-tests/006-image-on-missing-device/test.sh b/tests/misc-tests/006-image-on-missing-device/test.sh
new file mode 100755
index 0000000..8680a70
--- /dev/null
+++ b/tests/misc-tests/006-image-on-missing-device/test.sh
@@ -0,0 +1,78 @@
+#!/bin/bash
+# test btrfs-image with a missing device (uses loop devices)
+#
+# - btrfs-image must not loop indefinetelly
+# - btrfs-image will expectedly fail to produce the dump
+
+source $TOP/tests/common
+
+check_prereq btrfs-show-super
+check_prereq btrfs-image
+check_prereq mkfs.btrfs
+check_prereq btrfs
+
+ndevs=2
+declare -a devs
+dev1=
+dev2=
+
+setup_root_helper
+
+
+# TODO: move the helpers to common
+
+prepare_devices()
+{
+ for i in `seq $ndevs`; do
+ touch img$i
+ chmod a+rw img$i
+ truncate -s0 img$i
+ truncate -s2g img$i
+ devs[$i]=`run_check_stdout $SUDO_HELPER losetup --find --show img$i`
+ done
+}
+
+cleanup_devices()
+{
+ for dev in ${devs[@]}; do
+ run_mayfail $SUDO_HELPER losetup -d $dev
+ done
+ for i in `seq $ndevs`; do
+ truncate -s0 img$i
+ done
+ run_check $SUDO_HELPER losetup --list
+}
+
+test_image_dump()
+{
+ run_check $SUDO_HELPER $TOP/btrfs check $dev1
+ # the output file will be deleted
+ run_mayfail $SUDO_HELPER $TOP/btrfs-image $dev1 /tmp/test-img.dump
+}
+
+test_run()
+{
+ run_check $SUDO_HELPER $TOP/mkfs.btrfs -f -d raid1 -m raid1 $dev1 $dev2
+
+ # we need extents to trigger reading from all devices
+ run_check $SUDO_HELPER mount $dev1 $TEST_MNT
+ run_check $SUDO_HELPER dd if=/dev/zero of=$TEST_MNT/a bs=1M count=10
+ run_check $SUDO_HELPER dd if=/dev/zero of=$TEST_MNT/b bs=4k count=1000 conv=sync
+ run_check $SUDO_HELPER umount $TEST_MNT
+
+ test_image_dump
+ run_check btrfs fi show $dev1
+ # create a degraded raid1 filesystem, check must succeed
+ # btrfs-image must not loop
+ run_mayfail wipefs -a $dev2
+ run_check $SUDO_HELPER losetup -d $dev2
+ run_check btrfs fi show $dev1
+
+ test_image_dump
+}
+
+prepare_devices
+dev1=${devs[1]}
+dev2=${devs[2]}
+test_run
+cleanup_devices
diff --git a/tests/misc-tests/007-subvolume-sync/test.sh b/tests/misc-tests/007-subvolume-sync/test.sh
new file mode 100755
index 0000000..a745fb5
--- /dev/null
+++ b/tests/misc-tests/007-subvolume-sync/test.sh
@@ -0,0 +1,32 @@
+#!/bin/bash
+# test btrfs subvolume run normally with more than one subvolume
+#
+# - btrfs subvolume must not loop indefinetelly
+# - btrfs subvolume return 0 in normal case
+
+source $TOP/tests/common
+
+check_prereq mkfs.btrfs
+check_prereq btrfs
+
+setup_root_helper
+prepare_test_dev
+
+run_check $SUDO_HELPER $TOP/mkfs.btrfs -f "$TEST_DEV"
+run_check_mount_test_dev
+
+# to check following thing in both 1 and multiple subvolume case:
+# 1: is subvolume sync loop indefinetelly
+# 2: is return value right
+#
+run_check $SUDO_HELPER $TOP/btrfs subvolume create "$TEST_MNT"/mysubvol1
+run_check $SUDO_HELPER $TOP/btrfs subvolume create "$TEST_MNT"/mysubvol2
+run_check $SUDO_HELPER $TOP/btrfs subvolume delete "$TEST_MNT"/mysubvol1
+run_check $SUDO_HELPER $TOP/btrfs subvolume delete "$TEST_MNT"/mysubvol2
+run_check $SUDO_HELPER $TOP/btrfs subvolume sync "$TEST_MNT"
+
+run_check $SUDO_HELPER $TOP/btrfs subvolume create "$TEST_MNT"/mysubvol
+run_check $SUDO_HELPER $TOP/btrfs subvolume delete "$TEST_MNT"/mysubvol
+run_check $SUDO_HELPER $TOP/btrfs subvolume sync "$TEST_MNT"
+
+run_check_umount_test_dev
diff --git a/utils.c b/utils.c
index 39b295a..f1e3248 100644
--- a/utils.c
+++ b/utils.c
@@ -35,6 +35,8 @@
#include <limits.h>
#include <blkid/blkid.h>
#include <sys/vfs.h>
+#include <sys/statfs.h>
+#include <linux/magic.h>
#include "kerncompat.h"
#include "radix-tree.h"
@@ -729,21 +731,18 @@ int btrfs_add_to_fsid(struct btrfs_trans_handle *trans,
struct btrfs_super_block *super = root->fs_info->super_copy;
struct btrfs_device *device;
struct btrfs_dev_item *dev_item;
- char *buf;
+ char *buf = NULL;
u64 total_bytes;
u64 num_devs;
int ret;
device = kzalloc(sizeof(*device), GFP_NOFS);
if (!device)
- return -ENOMEM;
- buf = kmalloc(sectorsize, GFP_NOFS);
- if (!buf) {
- kfree(device);
- return -ENOMEM;
- }
+ goto err_nomem;
+ buf = kzalloc(sectorsize, GFP_NOFS);
+ if (!buf)
+ goto err_nomem;
BUG_ON(sizeof(*disk_super) > sectorsize);
- memset(buf, 0, sectorsize);
disk_super = (struct btrfs_super_block *)buf;
dev_item = &disk_super->dev_item;
@@ -761,6 +760,8 @@ int btrfs_add_to_fsid(struct btrfs_trans_handle *trans,
device->total_ios = 0;
device->dev_root = root->fs_info->dev_root;
device->name = strdup(path);
+ if (!device->name)
+ goto err_nomem;
ret = btrfs_add_device(trans, root, device);
BUG_ON(ret);
@@ -790,42 +791,59 @@ int btrfs_add_to_fsid(struct btrfs_trans_handle *trans,
list_add(&device->dev_list, &root->fs_info->fs_devices->devices);
device->fs_devices = root->fs_info->fs_devices;
return 0;
+
+err_nomem:
+ kfree(device);
+ kfree(buf);
+ return -ENOMEM;
}
-static void btrfs_wipe_existing_sb(int fd)
+static int btrfs_wipe_existing_sb(int fd)
{
const char *off = NULL;
size_t len = 0;
loff_t offset;
char buf[BUFSIZ];
- int rc = 0;
+ int ret = 0;
blkid_probe pr = NULL;
pr = blkid_new_probe();
if (!pr)
- return;
+ return -1;
- if (blkid_probe_set_device(pr, fd, 0, 0))
+ if (blkid_probe_set_device(pr, fd, 0, 0)) {
+ ret = -1;
goto out;
+ }
- rc = blkid_probe_lookup_value(pr, "SBMAGIC_OFFSET", &off, NULL);
- if (!rc)
- rc = blkid_probe_lookup_value(pr, "SBMAGIC", NULL, &len);
+ ret = blkid_probe_lookup_value(pr, "SBMAGIC_OFFSET", &off, NULL);
+ if (!ret)
+ ret = blkid_probe_lookup_value(pr, "SBMAGIC", NULL, &len);
- if (rc || len == 0 || off == NULL)
+ if (ret || len == 0 || off == NULL) {
+ /*
+ * If lookup fails, the probe did not find any values, eg. for
+ * a file image or a loop device. Soft error.
+ */
+ ret = 1;
goto out;
+ }
offset = strtoll(off, NULL, 10);
if (len > sizeof(buf))
len = sizeof(buf);
memset(buf, 0, len);
- rc = pwrite(fd, buf, len, offset);
+ ret = pwrite(fd, buf, len, offset);
+ if (ret != len) {
+ fprintf(stderr, "ERROR: cannot wipe existing superblock\n");
+ ret = -1;
+ }
fsync(fd);
out:
blkid_free_probe(pr);
- return;
+ return ret;
}
int btrfs_prepare_device(int fd, char *file, int zero_end, u64 *block_count_ret,
@@ -879,7 +897,12 @@ int btrfs_prepare_device(int fd, char *file, int zero_end, u64 *block_count_ret,
return 1;
}
- btrfs_wipe_existing_sb(fd);
+ ret = btrfs_wipe_existing_sb(fd);
+ if (ret < 0) {
+ fprintf(stderr, "ERROR: cannot wipe superblocks on '%s'\n",
+ file);
+ return 1;
+ }
*block_count_ret = block_count;
return 0;
@@ -936,7 +959,7 @@ int is_block_device(const char *path)
if (stat(path, &statbuf) < 0)
return -errno;
- return S_ISBLK(statbuf.st_mode);
+ return !!S_ISBLK(statbuf.st_mode);
}
/*
@@ -1063,7 +1086,8 @@ int open_path_or_dev_mnt(const char *path, DIR **dirstream)
char mp[PATH_MAX];
int fdmnt;
- if (is_block_device(path)) {
+ fdmnt = is_block_device(path);
+ if (fdmnt == 1) {
int ret;
ret = get_btrfs_mount(path, mp, sizeof(mp));
@@ -1073,13 +1097,67 @@ int open_path_or_dev_mnt(const char *path, DIR **dirstream)
return -1;
}
fdmnt = open_file_or_dir(mp, dirstream);
- } else {
+ } else if (fdmnt == 0) {
fdmnt = open_file_or_dir(path, dirstream);
}
return fdmnt;
}
+/*
+ * Do the following checks before calling open_file_or_dir():
+ * 1: path is in a btrfs filesystem
+ * 2: path is a directory
+ */
+int btrfs_open_dir(const char *path, DIR **dirstream, int verbose)
+{
+ struct statfs stfs;
+ struct stat st;
+ int ret;
+
+ if (statfs(path, &stfs) != 0) {
+ if (verbose)
+ fprintf(stderr,
+ "ERROR: can't access '%s': %s\n",
+ path, strerror(errno));
+ return -1;
+ }
+
+ if (stfs.f_type != BTRFS_SUPER_MAGIC) {
+ if (verbose)
+ fprintf(stderr,
+ "ERROR: not a btrfs filesystem: %s\n",
+ path);
+ return -2;
+ }
+
+ if (stat(path, &st) != 0) {
+ if (verbose)
+ fprintf(stderr,
+ "ERROR: can't access '%s': %s\n",
+ path, strerror(errno));
+ return -1;
+ }
+
+ if (!S_ISDIR(st.st_mode)) {
+ if (verbose)
+ fprintf(stderr,
+ "ERROR: not a directory: %s\n",
+ 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));
+ }
+
+ return ret;
+}
+
/* checks if a device is a loop device */
static int is_loop_device (const char* device) {
struct stat statbuf;
@@ -1663,7 +1741,7 @@ static int set_label_mounted(const char *mount_path, const char *label)
return 0;
}
-static int get_label_unmounted(const char *dev, char *label)
+int get_label_unmounted(const char *dev, char *label)
{
struct btrfs_root *root;
int ret;
@@ -1673,11 +1751,6 @@ static int get_label_unmounted(const char *dev, char *label)
fprintf(stderr, "FATAL: error checking %s mount status\n", dev);
return -1;
}
- if (ret > 0) {
- fprintf(stderr, "ERROR: dev %s is mounted, use mount point\n",
- dev);
- return -1;
- }
/* Open the super_block at the default location
* and as read-only.
@@ -1702,6 +1775,7 @@ int get_label_mounted(const char *mount_path, char *labelp)
{
char label[BTRFS_LABEL_SIZE];
int fd;
+ int ret;
fd = open(mount_path, O_RDONLY | O_NOATIME);
if (fd < 0) {
@@ -1710,10 +1784,14 @@ int get_label_mounted(const char *mount_path, char *labelp)
}
memset(label, '\0', sizeof(label));
- if (ioctl(fd, BTRFS_IOC_GET_FSLABEL, label) < 0) {
- fprintf(stderr, "ERROR: unable get label %s\n", strerror(errno));
+ ret = ioctl(fd, BTRFS_IOC_GET_FSLABEL, label);
+ if (ret < 0) {
+ if (errno != ENOTTY)
+ fprintf(stderr, "ERROR: unable to get label %s\n",
+ strerror(errno));
+ ret = -errno;
close(fd);
- return -1;
+ return ret;
}
strncpy(labelp, label, sizeof(label));
@@ -2061,7 +2139,7 @@ int get_fs_info(char *path, struct btrfs_ioctl_fs_info_args *fi_args,
memset(fi_args, 0, sizeof(*fi_args));
- if (is_block_device(path)) {
+ if (is_block_device(path) == 1) {
struct btrfs_super_block *disk_super;
char buf[BTRFS_SUPER_INFO_SIZE];
u64 devid;
@@ -2390,7 +2468,7 @@ int test_num_disk_vs_raid(u64 metadata_profile, u64 data_profile,
if (!mixed && (data_profile & BTRFS_BLOCK_GROUP_DUP)) {
fprintf(stderr,
- "ERROR: DUP for data is allowed only in mixed mode");
+ "ERROR: DUP for data is allowed only in mixed mode\n");
return 1;
}
return 0;
@@ -2473,7 +2551,7 @@ int test_dev_for_mkfs(char *file, int force_overwrite)
return 0;
}
-int btrfs_scan_lblkid()
+int btrfs_scan_lblkid(void)
{
int fd = -1;
int ret;
@@ -2851,7 +2929,7 @@ int btrfs_tree_search2_ioctl_supported(int fd)
return v2_supported;
}
-int btrfs_check_nodesize(u32 nodesize, u32 sectorsize)
+int btrfs_check_nodesize(u32 nodesize, u32 sectorsize, u64 features)
{
if (nodesize < sectorsize) {
fprintf(stderr,
@@ -2868,6 +2946,12 @@ int btrfs_check_nodesize(u32 nodesize, u32 sectorsize)
"ERROR: Illegal nodesize %u (not aligned to %u)\n",
nodesize, sectorsize);
return -1;
+ } else if (features & BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS &&
+ nodesize != sectorsize) {
+ fprintf(stderr,
+ "ERROR: Illegal nodesize %u (not equal to %u for mixed block group)\n",
+ nodesize, sectorsize);
+ return -1;
}
return 0;
}
@@ -2890,3 +2974,105 @@ int arg_copy_path(char *dest, const char *src, int destlen)
return 0;
}
+
+unsigned int get_unit_mode_from_arg(int *argc, char *argv[], int df_mode)
+{
+ unsigned int unit_mode = UNITS_DEFAULT;
+ int arg_i;
+ int arg_end;
+
+ for (arg_i = 0; arg_i < *argc; arg_i++) {
+ if (!strcmp(argv[arg_i], "--raw")) {
+ unit_mode = UNITS_RAW;
+ argv[arg_i] = NULL;
+ continue;
+ }
+ if (!strcmp(argv[arg_i], "--human-readable")) {
+ unit_mode = UNITS_HUMAN_BINARY;
+ argv[arg_i] = NULL;
+ continue;
+ }
+
+ if (!strcmp(argv[arg_i], "--iec")) {
+ units_set_mode(&unit_mode, UNITS_BINARY);
+ argv[arg_i] = NULL;
+ continue;
+ }
+ if (!strcmp(argv[arg_i], "--si")) {
+ units_set_mode(&unit_mode, UNITS_DECIMAL);
+ argv[arg_i] = NULL;
+ continue;
+ }
+
+ if (!strcmp(argv[arg_i], "--kbytes")) {
+ units_set_base(&unit_mode, UNITS_KBYTES);
+ argv[arg_i] = NULL;
+ continue;
+ }
+ if (!strcmp(argv[arg_i], "--mbytes")) {
+ units_set_base(&unit_mode, UNITS_MBYTES);
+ argv[arg_i] = NULL;
+ continue;
+ }
+ if (!strcmp(argv[arg_i], "--gbytes")) {
+ units_set_base(&unit_mode, UNITS_GBYTES);
+ argv[arg_i] = NULL;
+ continue;
+ }
+ if (!strcmp(argv[arg_i], "--tbytes")) {
+ units_set_base(&unit_mode, UNITS_TBYTES);
+ argv[arg_i] = NULL;
+ continue;
+ }
+
+ if (!df_mode)
+ continue;
+
+ if (!strcmp(argv[arg_i], "-b")) {
+ unit_mode = UNITS_RAW;
+ argv[arg_i] = NULL;
+ continue;
+ }
+ if (!strcmp(argv[arg_i], "-h")) {
+ unit_mode = UNITS_HUMAN_BINARY;
+ argv[arg_i] = NULL;
+ continue;
+ }
+ if (!strcmp(argv[arg_i], "-H")) {
+ unit_mode = UNITS_HUMAN_DECIMAL;
+ argv[arg_i] = NULL;
+ continue;
+ }
+ if (!strcmp(argv[arg_i], "-k")) {
+ units_set_base(&unit_mode, UNITS_KBYTES);
+ argv[arg_i] = NULL;
+ continue;
+ }
+ if (!strcmp(argv[arg_i], "-m")) {
+ units_set_base(&unit_mode, UNITS_MBYTES);
+ argv[arg_i] = NULL;
+ continue;
+ }
+ if (!strcmp(argv[arg_i], "-g")) {
+ units_set_base(&unit_mode, UNITS_GBYTES);
+ argv[arg_i] = NULL;
+ continue;
+ }
+ if (!strcmp(argv[arg_i], "-t")) {
+ units_set_base(&unit_mode, UNITS_TBYTES);
+ argv[arg_i] = NULL;
+ continue;
+ }
+ }
+
+ for (arg_i = 0, arg_end = 0; arg_i < *argc; arg_i++) {
+ if (!argv[arg_i])
+ continue;
+ argv[arg_end] = argv[arg_i];
+ arg_end++;
+ }
+
+ *argc = arg_end;
+
+ return unit_mode;
+}
diff --git a/utils.h b/utils.h
index 94606ed..192f3d1 100644
--- a/utils.h
+++ b/utils.h
@@ -158,11 +158,13 @@ int is_block_device(const char *file);
int is_mount_point(const char *file);
int check_arg_type(const char *input);
int open_path_or_dev_mnt(const char *path, DIR **dirstream);
+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);
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 group_profile_max_safe_loss(u64 flags);
@@ -240,8 +242,32 @@ static inline u64 div_factor(u64 num, int factor)
}
int btrfs_tree_search2_ioctl_supported(int fd);
-int btrfs_check_nodesize(u32 nodesize, u32 sectorsize);
+int btrfs_check_nodesize(u32 nodesize, u32 sectorsize, u64 features);
const char *get_argv0_buf(void);
+#define HELPINFO_OUTPUT_UNIT \
+ "--raw raw numbers in bytes", \
+ "--human-readable human friendly numbers, base 1024 (default)", \
+ "--iec use 1024 as a base (KiB, MiB, GiB, TiB)", \
+ "--si use 1000 as a base (kB, MB, GB, TB)", \
+ "--kbytes show sizes in KiB, or kB with --si", \
+ "--mbytes show sizes in MiB, or MB with --si", \
+ "--gbytes show sizes in GiB, or GB with --si", \
+ "--tbytes show sizes in TiB, or TB with --si"
+
+#define HELPINFO_OUTPUT_UNIT_DF \
+ "-b|--raw raw numbers in bytes", \
+ "-h|--human-readable", \
+ " human friendly numbers, base 1024 (default)", \
+ "-H human friendly numbers, base 1000", \
+ "--iec use 1024 as a base (KiB, MiB, GiB, TiB)", \
+ "--si use 1000 as a base (kB, MB, GB, TB)", \
+ "-k|--kbytes show sizes in KiB, or kB with --si", \
+ "-m|--mbytes show sizes in MiB, or MB with --si", \
+ "-g|--gbytes show sizes in GiB, or GB with --si", \
+ "-t|--tbytes show sizes in TiB, or TB with --si"
+
+unsigned int get_unit_mode_from_arg(int *argc, char *argv[], int df_mode);
+
#endif
diff --git a/version.sh b/version.sh
index 2ce324f..939ae9a 100755
--- a/version.sh
+++ b/version.sh
@@ -6,7 +6,7 @@
# Copyright 2008, Oracle
# Released under the GNU GPLv2
-v="v4.1.2"
+v="v4.2.2"
opt=$1
diff --git a/volumes.c b/volumes.c
index f7462c5..ca50f1c 100644
--- a/volumes.c
+++ b/volumes.c
@@ -198,6 +198,17 @@ again:
return 0;
}
+void btrfs_close_all_devices(void)
+{
+ struct btrfs_fs_devices *fs_devices;
+
+ while (!list_empty(&fs_uuids)) {
+ fs_devices = list_entry(fs_uuids.next, struct btrfs_fs_devices,
+ list);
+ btrfs_close_devices(fs_devices);
+ }
+}
+
int btrfs_open_devices(struct btrfs_fs_devices *fs_devices, int flags)
{
int fd;
diff --git a/volumes.h b/volumes.h
index 99a3fa1..4ecb993 100644
--- a/volumes.h
+++ b/volumes.h
@@ -148,6 +148,16 @@ struct map_lookup {
#define BTRFS_RAID5_P_STRIPE ((u64)-2)
#define BTRFS_RAID6_Q_STRIPE ((u64)-1)
+/*
+ * Check if the given range cross stripes.
+ * To ensure kernel scrub won't causing bug on with METADATA in mixed
+ * block group
+ */
+static inline int check_crossing_stripes(u64 start, u64 len)
+{
+ return (start / BTRFS_STRIPE_LEN) !=
+ ((start + len - 1) / BTRFS_STRIPE_LEN);
+}
int __btrfs_map_block(struct btrfs_mapping_tree *map_tree, int rw,
u64 logical, u64 *length, u64 *type,
@@ -177,6 +187,7 @@ int btrfs_add_device(struct btrfs_trans_handle *trans,
int btrfs_open_devices(struct btrfs_fs_devices *fs_devices,
int flags);
int btrfs_close_devices(struct btrfs_fs_devices *fs_devices);
+void btrfs_close_all_devices(void);
int btrfs_add_device(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct btrfs_device *device);