summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDimitri John Ledkov <dimitri.j.ledkov@linux.intel.com>2015-11-07 11:26:50 +0000
committerDimitri John Ledkov <dimitri.j.ledkov@linux.intel.com>2015-11-07 11:26:50 +0000
commitfb7fefdc77df5ff44df44109f46a3b8a00408b07 (patch)
tree4e2c1c87463019abe320876c5e9655a7861341a7
parent4883faaafb4ae60938b83f7c9cf4c847fd2b21a6 (diff)
New upstream release 4.3.
-rw-r--r--.gitignore1
-rw-r--r--Documentation/Makefile.in11
-rw-r--r--Documentation/btrfs-balance.asciidoc143
-rw-r--r--Documentation/btrfs-check.asciidoc2
-rw-r--r--Documentation/btrfs-convert.asciidoc85
-rw-r--r--Documentation/btrfs-inspect-internal.asciidoc50
-rw-r--r--Documentation/btrfs-show-super.asciidoc4
-rw-r--r--Documentation/btrfstune.asciidoc19
-rw-r--r--Documentation/mkfs.btrfs.asciidoc341
-rw-r--r--Makefile.extrawarn90
-rw-r--r--Makefile.in53
-rw-r--r--README.md41
-rw-r--r--backref.c8
-rw-r--r--btrfs-calc-size.c14
-rw-r--r--btrfs-convert.c25
-rw-r--r--btrfs-debug-tree.c5
-rw-r--r--btrfs-find-root.c1
-rw-r--r--btrfs-fragments.c6
-rw-r--r--btrfs-image.c11
-rw-r--r--btrfs-list.c9
-rw-r--r--btrfs-map-logical.c1
-rw-r--r--btrfs-select-super.c3
-rw-r--r--btrfs-show-super.c7
-rw-r--r--btrfs.c41
-rw-r--r--btrfstune.c1
-rw-r--r--chunk-recover.c15
-rw-r--r--cmds-balance.c165
-rw-r--r--cmds-check.c205
-rw-r--r--cmds-device.c29
-rw-r--r--cmds-fi-usage.c112
-rw-r--r--cmds-filesystem.c25
-rw-r--r--cmds-inspect.c26
-rw-r--r--cmds-qgroup.c23
-rw-r--r--cmds-quota.c12
-rw-r--r--cmds-replace.c47
-rw-r--r--cmds-restore.c2
-rw-r--r--cmds-scrub.c196
-rw-r--r--cmds-subvolume.c58
-rwxr-xr-xconfigure18
-rw-r--r--ctree.c29
-rw-r--r--disk-io.c180
-rw-r--r--extent-tree.c4
-rw-r--r--extent_io.c3
-rw-r--r--inode-map.c1
-rw-r--r--mkfs.c78
-rw-r--r--print-tree.c13
-rw-r--r--qgroup.c10
-rw-r--r--quick-test.c3
-rw-r--r--string-table.c74
-rw-r--r--string-table.h8
-rw-r--r--tests/README.md136
-rw-r--r--tests/common112
-rw-r--r--tests/fsck-tests/018-leaf-crossing-stripes/default_case.raw.xzbin0 -> 105064 bytes
-rwxr-xr-xtests/fsck-tests/018-leaf-crossing-stripes/test.sh12
-rwxr-xr-xtests/fuzz-tests.sh41
-rwxr-xr-xtests/fuzz-tests/001-simple-unmounted/test.sh20
-rwxr-xr-xtests/misc-tests/008-leaf-crossing-stripes/test.sh25
-rwxr-xr-xtests/misc-tests/009-subvolume-sync-must-wait/test.sh50
-rwxr-xr-xtests/misc-tests/010-convert-delete-ext2-subvol/test.sh24
-rwxr-xr-xtests/mkfs-tests.sh44
-rwxr-xr-xtests/mkfs-tests/001-basic-profiles/test.sh89
-rwxr-xr-xtests/mkfs-tests/002-no-force-mixed-on-small-volume/test.sh13
-rwxr-xr-xtests/mkfs-tests/003-mixed-with-wrong-nodesize/test.sh12
-rwxr-xr-xtests/mkfs-tests/004-rootdir-keeps-size/test.sh30
-rw-r--r--utils.c50
-rw-r--r--utils.h60
-rwxr-xr-xversion.sh2
-rw-r--r--volumes.c3
68 files changed, 2210 insertions, 821 deletions
diff --git a/.gitignore b/.gitignore
index 77506a0..a27cb0d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,6 +7,7 @@ version.h
version
man/*.gz
Documentation/*.gz
+Documentation/*.html
btrfs
btrfs.static
btrfs-debug-tree
diff --git a/Documentation/Makefile.in b/Documentation/Makefile.in
index b03eaf2..4711ad1 100644
--- a/Documentation/Makefile.in
+++ b/Documentation/Makefile.in
@@ -36,6 +36,7 @@ MAN5_TXT += btrfs-mount.asciidoc
MAN_TXT = $(MAN8_TXT) $(MAN5_TXT)
MAN_XML = $(patsubst %.asciidoc,%.xml,$(MAN_TXT))
+MAN_HTML = $(patsubst %.asciidoc,%.html,$(MAN_TXT))
DOC_MAN5 = $(patsubst %.asciidoc,%.5,$(MAN5_TXT))
GZ_MAN5 = $(patsubst %.asciidoc,%.5.gz,$(MAN5_TXT))
@@ -81,6 +82,7 @@ all: man
man: man5 man8
man5: $(GZ_MAN5)
man8: $(GZ_MAN8)
+html: $(MAN_HTML)
install: install-man
@@ -100,7 +102,7 @@ uninstall:
$(RMDIR) -p --ignore-fail-on-non-empty $(DESTDIR)$(man8dir)
clean:
- $(QUIET_RM)$(RM) -f *.xml *.xml+ *.5 *.5.gz *.8 *.8.gz
+ $(QUIET_RM)$(RM) -f *.xml *.xml+ *.5 *.5.gz *.8 *.8.gz *.html
%.5.gz : %.5
$(QUIET_GZIP)$(GZIPCMD) -n -c $< > $@
@@ -122,3 +124,10 @@ clean:
$(ASCIIDOC_EXTRA) -abtrfs_version=$(BTRFS_VERSION) \
-o $@+ $< && \
$(MV) $@+ $@
+
+%.html : %.asciidoc asciidoc.conf
+ $(QUIET_ASCIIDOC)$(RM) -f $@+ $@ && \
+ $(ASCIIDOC) -b html -d article -f asciidoc.conf \
+ $(ASCIIDOC_EXTRA) -abtrfs_version=$(BTRFS_VERSION) \
+ -o $@+ $< && \
+ $(MV) $@+ $@
diff --git a/Documentation/btrfs-balance.asciidoc b/Documentation/btrfs-balance.asciidoc
index 6d2fd0c..7374a53 100644
--- a/Documentation/btrfs-balance.asciidoc
+++ b/Documentation/btrfs-balance.asciidoc
@@ -3,7 +3,7 @@ btrfs-balance(8)
NAME
----
-btrfs-balance - balance btrfs filesystem
+btrfs-balance - balance block groups on a btrfs filesystem
SYNOPSIS
--------
@@ -11,10 +11,37 @@ SYNOPSIS
DESCRIPTION
-----------
-*btrfs balance* is used to balance chunks in a btrfs filesystem across
-multiple or even single device.
+The primary purpose of the balance feature is to spread block groups accross
+all devices so they match constraints defined by the respective profiles. See
+`mkfs.btrfs`(8) section 'PROFILES' for more details.
+The scope of the balancing process can be further tuned by use of filters that
+can select the block groups to process. Balance works only on a mounted
+filesystem.
-See `btrfs-device`(8) for more details about the effect on device management.
+The balance operation is cancellable by the user. The on-disk state of the
+filesystem is always consistent so an unexpected interruption (eg. system crash,
+reboot) does not corrupt the filesystem. The progress of the balance operation
+is temporarily stored and will be resumed upon mount, unless the mount option
+'skip_balance' is specified.
+
+WARNING: running balance without filters will take a lot of time as it basically
+rewrites the entire filesystem and needs to update all block pointers.
+
+The filters can be used to perform following actions:
+
+- convert block group profiles (filter 'convert')
+- make block group usage more compact (filter 'usage')
+- perform actions only on a given device (filters 'devid', 'drange')
+
+The filters can be applied to a combination of block group types (data,
+metadata, system). Note that changing 'system' needs the force option.
+
+NOTE: the balance operation needs enough work space, ie. space that is
+completely unused in the filesystem, otherwise this may lead to ENOSPC reports.
+See the section 'ENOSPC' for more details.
+
+COMPATIBILITY
+-------------
NOTE: The balance subcommand also exists under the *filesystem* namespace. This
still works for backward compatibility but is deprecated and should not be
@@ -27,36 +54,31 @@ command instead.
SUBCOMMAND
----------
*cancel* <path>::
-Cancel running or paused balance.
+cancel running or paused balance
*pause* <path>::
-Pause running balance.
+pause running balance operation, this will store the state of the balance
+progress and used filters to the filesystem
*resume* <path>::
-Resume interrupted balance.
+resume interrupted balance
*start* [options] <path>::
-Balance chunks across the devices *online*.
-+
-Balance and/or convert (change allocation profile of) chunks that
-passed all filters in a comma-separated list of filters for a
-particular chunk type.
-If filter list is not given balance all chunks of that type.
-In case none of the -d, -m or -s options is
-given balance all chunks in a filesystem.
+start the balance operation according to the specified filters, no filters
+will rewrite the entire filesystem. The process runs in the foreground.
+
`Options`
+
-d[<filters>]::::
-act on data chunks. See `FILTERS` section for details about <filters>.
+act on data block groups, see `FILTERS` section for details about 'filters'
-m[<filters>]::::
-act on metadata chunks. See `FILTERS` section for details about <filters>.
+act on metadata chunks, see `FILTERS` section for details about 'filters'
-s[<filters>]::::
-act on system chunks (only under -f). See `FILTERS` section for details about <filters>.
+act on system chunks (requires '-f'), see `FILTERS` section for details about 'filters'.
-v::::
-be verbose
+be verbose and print balance filter arguments
-f::::
-force reducing of metadata integrity
+force reducing of metadata integrity, eg. when going from 'raid1' to 'single'
*status* [-v] <path>::
Show status of running or paused balance.
@@ -67,61 +89,92 @@ FILTERS
-------
From kernel 3.3 onwards, btrfs balance can limit its action to a subset of the
full filesystem, and can be used to change the replication configuration (e.g.
-moving data from single to RAID-1). This functionality is accessed through the
+moving data from single to RAID1). This functionality is accessed through the
'-d', '-m' or '-s' options to btrfs balance start, which filter on data,
metadata and system blocks respectively.
-A filter has the following stucture: ::
-'type'[='params'][,'type'=...]
+A filter has the following stucture: 'type'[='params'][,'type'=...]
The available types are:
-*profiles*::
-Balances only block groups with the given replication profiles. Parameters
-are a list of profile names separated by |.
+*profiles=<profiles>*::
+Balances only block groups with the given profiles. Parameters
+are a list of profile names separated by "'|'" (pipe).
-*usage*::
+*usage=<percent>*::
Balances only block groups with usage under the given percentage. The
value of 0 is allowed and will clean up completely unused block groups, this
-should not require any new space allocated. You may want to use usage=0 in
+should not require any new space allocated. You may want to use 'usage=0' in
case balance is returnin ENOSPC and your filesystem is not too full.
-*devid*::
+*devid=<id>*::
Balances only block groups which have at least one chunk on the given
-device (by btrfs device ID -- use btrfs fi show to list device IDs)
+device. To list devices with ids use 'btrfs fi show'.
-*drange*::
+*drange=<range>*::
Balances only block groups which overlap with the given byte range on any
-device. (Use in conjunction with "devid" to filter on a specific device). The
-parameter is a range specified as <start..end>.
+device.(Use in conjunction with 'devid' to filter on a specific device. The
+parameter is a range specified as 'start..end'.
-*vrange*::
+*vrange=<range>*::
Balances only block groups which overlap with the given byte range in the
filesystem's internal virtual address space. This is the address space that
most reports from btrfs in the kernel log use. The parameter is a range
-specified as <start..end>.
+specified as 'start..end'.
-*convert*::
+*convert=<profile>*::
Convert each selected block group to the given profile name identified by
parameters.
-*limit*::
-Process only given number of chunks, after all filters apply. This can be used
-to specifically target a chunk in connection with other filters (drange,
+*limit=<number>*::
+Process only given number of chunks, after all filters are applied. This can be
+used to specifically target a chunk in connection with other filters (drange,
vrange) or just simply limit the amount of work done by a single balance run.
*soft*::
Takes no parameters. Only has meaning when converting between profiles.
When doing convert from one profile to another and soft mode is on,
-restriper won't touch chunks that already have the target profile. This is
-useful if e.g. half of the FS was converted earlier.
+chunks that alread yave the target profile are left untouched
+ This is useful if e.g. half of the filesystem was converted earlier.
+
-The soft mode switch is (like every other filter) per-type. This means
-that we can convert for example meta chunks the "hard" way while converting
-data chunks selectively with soft switch.
+The soft mode switch is (like every other filter) per-type.
+For example, this means that we can convert metadata chunks the "hard" way
+while converting data chunks selectively with soft switch.
Profile names, used in profiles and convert are one of: 'raid0', 'raid1',
-'raid10', 'raid5', 'raid6', 'dup', 'single'.
+'raid10', 'raid5', 'raid6', 'dup', 'single'. The mixed data/metadata profiles
+can be converted in the same bay, but it's conversion between mixed and non-mixed
+is not implemented.
+
+ENOSPC
+------
+
+The way balance operates, it usually needs to temporarily create a new block
+group and move the old data there. For that it needs work space, otherwise
+it fails for ENOSPC reasons.
+This is not the same ENOSPC as if the free space is exhausted. This refers to
+the space on the level of block groups.
+
+The free work space can be calculated from the output of the 'btrfs filesystem show'
+command:
+
+ Label: 'BTRFS' uuid: 8a9d72cd-ead3-469d-b371-9c7203276265
+ Total devices 2 FS bytes used 77.03GiB
+ devid 1 size 53.90GiB used 51.90GiB path /dev/sdc2
+ devid 2 size 53.90GiB used 51.90GiB path /dev/sde1
+
+'size' - 'used' = 'free work space' +
+'53.90GiB' - '51.90GiB' = '2.00GiB'
+
+An example of a filter that does not require workspace is 'usage=0'. This will
+scan through all unused block groups of a given type and will reclaim the
+space. Ater that it might be possible to run other filters.
+
+**CONVERSIONS ON MULTIPLE DEVICES**
+
+Conversion to profiles based on striping (RAID0, RAID5/6) require the work
+space on each device. An interrupted balance may leave partially filled block
+groups that might consume the work space.
EXIT STATUS
-----------
diff --git a/Documentation/btrfs-check.asciidoc b/Documentation/btrfs-check.asciidoc
index f8d8ab4..a929862 100644
--- a/Documentation/btrfs-check.asciidoc
+++ b/Documentation/btrfs-check.asciidoc
@@ -33,6 +33,8 @@ create a new CRC tree and recalculate all checksums
create a new extent tree
--check-data-csum::
verify checkums of data blocks
+-p|--progress::
+indicate progress at various checking phases
--qgroup-report::
verify qgroup accounting and compare against filesystem accounting
--subvol-extents <subvolid>::
diff --git a/Documentation/btrfs-convert.asciidoc b/Documentation/btrfs-convert.asciidoc
index 09510fa..ca3417f 100644
--- a/Documentation/btrfs-convert.asciidoc
+++ b/Documentation/btrfs-convert.asciidoc
@@ -3,7 +3,7 @@ btrfs-convert(8)
NAME
----
-btrfs-convert - convert from ext2/3/4 filesystem to btrfs or rollback
+btrfs-convert - convert from ext2/3/4 filesystem to btrfs
SYNOPSIS
--------
@@ -11,42 +11,82 @@ SYNOPSIS
DESCRIPTION
-----------
-*btrfs-convert* is used to convert existed ext2/3/4 to btrfs filesystem,
-and the original filesystem image is accessible as from separate subvolume
-named 'ext2_saved' as file image.
+*btrfs-convert* is used to convert existing ext2/3/4 filesystem image to a
+btrfs filesystem in-place. The original filesystem image is accessible
+subvolume named 'ext2_saved' as file 'image'.
-WARNING: If one hopes to rollback to ext2/3/4, they should not execute
-*btrfs balance* command on converted btrfs, since it will change the extent
-layout and make *btrfs-convert* unable to rollback.
+WARNING: If you are going to perform rollback to ext2/3/4, you should not
+execute *btrfs balance* command on the converted filesystem. This will change
+the extent layout and make *btrfs-convert* unable to rollback.
-NOTE: If one is satisfied with converted btrfs, and not longer wants to
-rollback to ext2/3/4, it is highly recommended to remove 'ext2_saved' subvolume
-and execute *btrfs filesystem defragment* and *btrfs balance* command on the
-converted btrfs.
+The conversion utilizes free space of the original filesystem. The exact
+estimate of the required space cannot be foretold. The final btrfs metadata
+might occupy several gigabytes on a hundreds-gigabyte filesystem.
+
+If you decide not to rollback anymore, it is recommended to perform a few more
+steps to transform the btrfs filesystem to a more compact layout. The
+conversion inherits the original data block fragmentation and the metadata
+blocks are bound to the original free space layout.
+
+**REMOVE THE ORIGINAL FILESYSTEM METADATA**
+
+By removing the 'ext2_saved' subvolume, all metadata of the original filesystem
+will be removed:
+
+ # btrfs subvolume delete /mnt/ext2_saved
+
+At this point it's not possible to do rollback. The filesystem is usable but may
+be impacted by the fragmentation.
+
+**MAKE FILE DATA MORE CONTIGUOUS**
+
+An optional but recommended step is to run defragmentation on the entire
+filesystem. This will attempt to make file extents more contiguous.
+
+ # btrfs filesystem defrag -v -r -f -t 32M /mnt/btrfs
+
+Verbose recursive defragmentation ('-v', '-r'), flush data per-file ('-f') with target
+extent size 32M ('-t').
+
+**ATTEMPT TO MAKE BTRFS METADATA MORE COMPACT**
+
+Optional but recommended step.
+
+The metadata block groups after conversion may be smaller than the default size
+(256MiB or 1GiB). Running a balance will attempt to merge the block groups.
+This depends on the free space layout (and fragmentation) and may fail. This is
+a soft error leaving the filesystem usable but the block group layout may
+remain unchanged.
+
+Note that balance operation takes a lot of time.
+
+ # btrfs balance start -m /mnt/btrfs
OPTIONS
-------
-d|--no-datasum::
-Disable data checksum calculations and set NODATASUM file flag. This can speed
-up the conversion.
+disable data checksum calculations and set NODATASUM file flag, this can speed
+up the conversion
-i|--no-xattr::
-Ignore xattrs and ACLs.
+ignore xattrs and ACLs of files
-n|--no-inline::
-Disable inlining of small files to metadata blocks.
+disable inlining of small files to metadata blocks, this will decrease the metadata
+consumption and may help to convert a filesystem with low free space
-N|--nodesize <SIZE>::
-Set filesystem nodesize, the tree block size in which btrfs stores data.
+set filesystem nodesize, the tree block size in which btrfs stores its metadata.
The default value is 16KB (16384) or the page size, whichever is bigger.
-Must be a multiple of the sectorsize, but not larger than 65536.
+Must be a multiple of the sectorsize, but not larger than 65536. Se
+`mkfs.btrfs`(8) for more details.
-r|--rollback::
-Roll back to ext2fs.
+rollback to the original ext2/3/4 filesystem if possible
-l|--label <LABEL>::
-set filesystem label during conversion.
+set filesystem label during conversion
-L|--copy-label::
-use label from the converted filesystem.
+use label from the converted filesystem
-p|--progress::
-Show progress of conversion, on by default.
+show progress of conversion, on by default
--no-progress::
-Disable detailed progress and show only the main phases of conversion.
+disable detailed progress and show only the main phases of conversion
EXIT STATUS
-----------
@@ -56,4 +96,3 @@ If any problems happened, 1 will be returned.
SEE ALSO
--------
`mkfs.btrfs`(8)
-
diff --git a/Documentation/btrfs-inspect-internal.asciidoc b/Documentation/btrfs-inspect-internal.asciidoc
index f3f915b..1c7c361 100644
--- a/Documentation/btrfs-inspect-internal.asciidoc
+++ b/Documentation/btrfs-inspect-internal.asciidoc
@@ -3,7 +3,7 @@ btrfs-inspect-internal(8)
NAME
----
-btrfs-inspect-internal - resolve different btrfs items for debug purpose
+btrfs-inspect-internal - query various internal information
SYNOPSIS
--------
@@ -11,53 +11,61 @@ SYNOPSIS
DESCRIPTION
-----------
-*btrfs inspect-internal* is used to resolve different items for debug purpose.
+
+This command group provides an interface to query internal information. The
+functionality ranges from a simple UI to an ioctl or a more complex query that
+assembles the result from several internal structures. The latter usually
+requires calls to privileged ioctls.
SUBCOMMAND
----------
-*inode-resolve* [-v] <inode> <path>::
-Resolves an <inode> in subvolume <path> to all filesystem paths.
+*inode-resolve* [-v] <ino> <path>::
+(needs root privileges)
++
+resolve paths to all files with given inode number 'ino' in a given subvolume
+at 'path', ie. all hardlinks
+
`Options`
+
-v::::
-verbose mode. print count of returned paths and ioctl() return value
+verbose mode, print count of returned paths and ioctl() return value
*logical-resolve* [-Pv] [-s <bufsize>] <logical> <path>::
-Resolves a <logical> address in the filesystem mounted at <path> to all inodes.
+(needs root privileges)
+
-By default, each inode is then resolved to a file system path (similar to the
-inode-resolve subcommand).
+resolve paths to all files at given 'logical' address in the linear filesystem space
+
`Options`
+
-P::::
skip the path resolving and print the inodes instead
-v::::
-verbose mode. print count of returned paths and all ioctl() return values
+verbose mode, print count of returned paths and all ioctl() return values
-s <bufsize>::::
-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.
+set internal buffer for storing the file names to 'bufsize', default is 4096, maximum 64k
*min-dev-size* [options] <path>::
-Return the minimum size the device can be shrunk to, without performing any
-resize operation.
+(needs root privileges)
++
+return the minimum size the device can be shrunk to, without performing any
+resize operation, this may be useful before executing the actual resize operation
+
`Options`
+
---id::::
-specify the device id to query, default is 1
+--id <id>::::
+specify the device 'id' to query, default is 1 if this option is not used
*rootid* <path>::
-For a given file or directory, return the containing tree root id. For a
-subvolume return it's own tree id.
+for a given file or directory, return the containing tree root id, for a
+subvolume itself return it's own tree id (ie. subvol id)
+
-The result is undefined for the so-called empty subvolumes (identified by inode number 2).
+NOTE: The result is undefined for the so-called empty subvolumes (identified by
+inode number 2), but such subvolume does not contain any files anyway
*subvolid-resolve* <subvolid> <path>::
-Get file system paths for the given subvolume ID.
+(needs root privileges)
++
+resolve the absolute path of a the subvolume id 'subvolid'
EXIT STATUS
-----------
diff --git a/Documentation/btrfs-show-super.asciidoc b/Documentation/btrfs-show-super.asciidoc
index 1646be3..8866c94 100644
--- a/Documentation/btrfs-show-super.asciidoc
+++ b/Documentation/btrfs-show-super.asciidoc
@@ -40,6 +40,10 @@ If several '-i <super_mirror>' are given, only the last one is valid.
Attempt to print the superblock even if no superblock magic is found. May end
badly.
+-s <bytenr>::
+specifiy offset to a superblock in a non-standard location at 'bytenr', useful
+for debugging (disables the '-f' option)
+
EXIT STATUS
-----------
*btrfs-show-super* will return 0 if no error happened.
diff --git a/Documentation/btrfstune.asciidoc b/Documentation/btrfstune.asciidoc
index db9a97b..f5cf15e 100644
--- a/Documentation/btrfstune.asciidoc
+++ b/Documentation/btrfstune.asciidoc
@@ -26,11 +26,14 @@ Enable seeding on a given device. Value 1 will enable seeding, 0 will disable it
A seeding filesystem is forced to be mounted read-only. A new device can be added
to the filesystem and will capture all writes keeping the seeding device intact.
-r::
-Enable extended inode refs (hardlink limit per file in a directory is 65536). Since kernel 3.7.
+Enable extended inode refs (hardlink limit per file in a directory is 65536),
+enabled by mkfs feature 'extref'. Since kernel 3.7.
-x::
-Enable skinny metadata extent refs (more efficient representation of extents). Since kernel 3.10.
+Enable skinny metadata extent refs (more efficient representation of extents),
+enabled by mkfs feature 'skinny-metadata'. Since kernel 3.10.
-n::
-Enable no-holes feature (more efficient representation of file holes). Since kernel 3.14.
+Enable no-holes feature (more efficient representation of file holes), enabled
+by mkfs feature 'no-holes'. Since kernel 3.14.
-f::
Allow dangerous changes, e.g. clear the seeding flag or change fsid. Make sure
that you are aware of the dangers.
@@ -38,12 +41,12 @@ that you are aware of the dangers.
Change fsid to a randomly generated UUID or continue previous fsid change
operation in case it was interrupted.
-U <UUID>::
-Change fsid to <UUID>.
+Change fsid to 'UUID'.
+
-The <UUID> should be a 36 bytes string in `printf`(3) format
-"%08x-%04x-%04x-%04x-%012x".
+The 'UUID' should be a 36 bytes string in `printf`(3) format
+'"%08x-%04x-%04x-%04x-%012x"'.
If there is a previous unfinished fsid change, it will continue only if the
-<UUID> matches the unfinished one or if you use the option '-u'.
+'UUID' matches the unfinished one or if you use the option '-u'.
WARNING: Cancelling or interrupting a UUID change operation will make the
filesystem temporarily unmountable. To fix it, rerun 'btrfstune -u' to restore
@@ -53,7 +56,7 @@ WARNING: Clearing the seeding flag on a device may be dangerous.
If a previously-seeding device is changed, all filesystems that used that
device will become unmountable. Setting the seeding flag back will not fix
that. +
-A valid usecase is "seeding device as a base image". Clear the seeding
+A valid usecase is 'seeding device as a base image'. Clear the seeding
flag, update the filesystem and make it seeding again, provided that it's ok
to throw away all filesystems built on top of the previous base.
diff --git a/Documentation/mkfs.btrfs.asciidoc b/Documentation/mkfs.btrfs.asciidoc
index a9aa3cb..12d8840 100644
--- a/Documentation/mkfs.btrfs.asciidoc
+++ b/Documentation/mkfs.btrfs.asciidoc
@@ -11,134 +11,301 @@ SYNOPSIS
$$[-A|--alloc-start <alloc-start>]$$
$$[-b|--byte-count <byte-count>]$$
$$[-d|--data <data-profile>]$$
-$$[-f|--force]$$
-$$[-n|--nodesize <nodesize>]$$
-$$[-l|--leafsize <leafsize>]$$
-$$[-L|--label <label>]$$
$$[-m|--metadata <metadata profile>]$$
$$[-M|--mixed]$$
-$$[-q|--quiet]$$
+$$[-l|--leafsize <leafsize>]$$
+$$[-n|--nodesize <nodesize>]$$
$$[-s|--sectorsize <sectorsize>]$$
-$$[-r|--rootdir <rootdir>]$$
+$$[-L|--label <label>]$$
$$[-K|--nodiscard]$$
+$$[-r|--rootdir <rootdir>]$$
$$[-O|--features <feature1>[,<feature2>...]]$$
$$[-U|--uuid <UUID>]$$
-$$[-h]$$
+$$[-f|--force]$$
+$$[-q|--quiet]$$
+$$[--help]$$
$$[-V|--version]$$
$$<device> [<device>...]$$
DESCRIPTION
-----------
-*mkfs.btrfs*
-is used to create a btrfs filesystem (usually in a disk partition, or an array
-of disk partitions).
+*mkfs.btrfs* is used to create the btrfs filesystem on a single or multiple
+devices. <device> is typically a block device but can be a file-backed image
+as well. Multiple devices are grouped by UUID of the filesystem.
-<device>
-is the special file corresponding to the device (e.g /dev/sdXX ).
-If multiple devices are specified, btrfs is created
-spanning across the specified devices.
+Before mounting such filesystem, the kernel module must know all the devices
+either via preceding execution of *btrfs device scan* or using the *device*
+mount option. See section *MULTIPLE DEVICES* for more details.
OPTIONS
-------
--A|--alloc-start <offset>::
-Specify the offset from the start of the device at which to start allocations
-in this btrfs filesystem. The default value is zero, or the start of the
-device. This option is intended only for debugging filesystem resize
-operations.
-
--b|--byte-count <size>::
-Specify the size of the resultant filesystem. If this option is not used,
-mkfs.btrfs uses all the available storage for the filesystem.
-
--d|--data <type>::
-Specify how the data must be spanned across the devices specified. Valid
-values are 'raid0', 'raid1', 'raid5', 'raid6', 'raid10' or 'single'.
-
--f|--force::
-Force overwrite when an existing filesystem is detected on the device.
-By default, mkfs.btrfs will not write to the device if it suspects that
-there is a filesystem or partition table on the device already.
-
--l|--leafsize <size>::
-Alias for --nodesize. Deprecated.
+*-A|--alloc-start <offset>*::
+(An option to help debugging chunk allocator.)
+Specify the (physical) offset from the start of the device at which allocations
+start. The default value is zero.
--n|--nodesize <size>::
-Specify the nodesize, the tree block size in which btrfs stores
-data. The default value is 16KB (16384) or the page size, whichever is
-bigger. Must be a multiple of the sectorsize, but not larger than 65536.
-Leafsize always equals nodesize and the options are aliases.
+*-b|--byte-count <size>*::
+Specify the size of the filesystem. If this option is not used,
+mkfs.btrfs uses the entire device space for the filesystem.
--L|--label <name>::
-Specify a label for the filesystem.
+*-d|--data <profile>*::
+Specify the profile for the data block groups. Valid values are 'raid0',
+'raid1', 'raid5', 'raid6', 'raid10' or 'single', (case does not matter).
+
+*-m|--metadata <profile>*::
+Specify the profile for the metadata block groups.
+Valid values are 'raid0', 'raid1', 'raid5', 'raid6', 'raid10', 'single' or
+'dup', (case does not matter).
++
+A single device filesystem will default to 'DUP', unless a SSD is detected. Then
+it will default to 'single'. The detection is based on the value of
+`/sys/block/DEV/queue/rotational`, where 'DEV' is the short name of the device.
+This is because SSDs can remap the blocks internally to a single copy thus
+deduplicating them which negates the purpose of increased metadata redunancy
+and just wastes space.
+
-NOTE: <name> should be less than 256 characters.
+Note that the rotational status can be arbitrarily set by the underlying block
+device driver and may not reflect the true status (network block device, memory-backed
+SCSI devices etc). Use the options '--data/--metadata' to avoid confusion.
--m|--metadata <profile>::
-Specify how metadata must be spanned across the devices specified. Valid
-values are 'raid0', 'raid1', 'raid5', 'raid6', 'raid10', 'single' or 'dup'.
+*-M|--mixed*::
+Normally the data and metadata block groups are isolated. The 'mixed' mode
+will remove the isolation and store both types in the same block group type.
+This helps to utilize the free space regardless of the purpose and is suitable
+for small devices. The separate allocation of block groups leads to a situation
+where the space is reserved for the other block group type, is not available for
+allocation and can lead to ENOSPC state.
+
-Single device
-will have dup set by default except in the case of SSDs which will default to
-single. This is because SSDs can remap blocks internally so duplicate blocks
-could end up in the same erase block which negates the benefits of doing
-metadata duplication.
-
--M|--mixed::
-Mix data and metadata chunks together for more efficient space
-utilization. This feature incurs a performance penalty in
-larger filesystems. It is recommended for use with filesystems
-of 1 GiB or smaller.
-
--q|--quiet::
-Print only error or warning messages. Options --features or --help are unaffected.
+The recommended size for the mixed mode is for filesystems less than 1GiB. The
+soft recommendation is to use it for filesystems smaller than 5GiB. Thie mixed
+mode may lead to degraded performance on larger filesystems, but is otherwise
+usable, even on multiple devices.
++
+The 'nodesize' and 'sectorsize' must be equal, and the block group types must
+match.
++
+NOTE: versions up to 4.2.x forced the mixed mode for devices smaller than 1GiB.
+This has been removed in 4.3+ as it caused some usability issues.
--s|--sectorsize <size>::
-Specify the sectorsize, the minimum data block allocation unit.
+*-l|--leafsize <size>*::
+Alias for --nodesize. Deprecated.
+
+*-n|--nodesize <size>*::
+Specify the nodesize, the tree block size in which btrfs stores metadata. The
+default value is 16KiB (16384) or the page size, whichever is bigger. Must be a
+multiple of the sectorsize, but not larger than 64KiB (65536). Leafsize always
+equals nodesize and the options are aliases.
+
-The default
-value is the page size. If the sectorsize differs from the page size, the
-created filesystem may not be mountable by current kernel. Therefore it is not
-recommended to use this option unless you are going to mount it on a system
-with the appropriate page size.
-
--r|--rootdir <rootdir>::
-Specify a directory to copy into the newly created btrfs filesystem.
+Smaller node size increases fragmentation but lead to higher b-trees which in
+turn leads to lower locking contention. Higher node sizes give better packing
+and less fragmentation at the cost of more expensive memory operations while
+updating the metadata blocks.
+
-NOTE: '-r' option is done completely in userland, and don't need root
-privilege to mount the filesystem.
+NOTE: versions up to 3.11 set the nodesize to 4k.
+
+*-s|--sectorsize <size>*::
+Specify the sectorsize, the minimum data block allocation unit.
++
+The default value is the page size and is autodetected. If the sectorsize
+differs from the page size, the created filesystem may not be mountable by the
+kernel. Therefore it is not recommended to use this option unless you are going
+to mount it on a system with the appropriate page size.
+
+*-L|--label <string>*::
+Specify a label for the filesystem. The 'string' should be less than 256
+bytes and must not contain newline characters.
+
+*-K|--nodiscard*::
+Do not perform whole device TRIM operation on devices that are capable of that.
--K|--nodiscard::
-Do not perform whole device TRIM operation by default.
+*-r|--rootdir <rootdir>*::
+Populate the toplevel subvolume with files from 'rootdir'. This does not
+require root permissions and does not mount the filesystem.
--O|--features <feature1>[,<feature2>...]::
+*-O|--features <feature1>[,<feature2>...]*::
A list of filesystem features turned on at mkfs time. Not all features are
supported by old kernels. To disable a feature, prefix it with '^'.
+
-To see all features run:
+See section *FILESYSTEM FEATURES* for more details. To see all available
+features that mkfs.btrfs supports run:
+
+mkfs.btrfs -O list-all+
--U|--uuid <UUID>::
-Create the filesystem with the specified UUID, which must not already exist on
-the system.
+*-f|--force*::
+Forcibly overwrite the block devices when an existing filesystem is detected.
+By default, mkfs.btrfs will utilize 'libblkid' to check for any known
+filesystem on the devices. Alternatively you can use the `wipefs` utility
+to clear the devices.
--V|--version::
+*-q|--quiet*::
+Print only error or warning messages. Options --features or --help are unaffected.
+
+*-U|--uuid <UUID>*::
+Create the filesystem with the given 'UUID'. The UUID must not exist on any
+filesystem currently present.
+
+*-V|--version*::
Print the *mkfs.btrfs* version and exit.
--h::
+*--help*::
Print help.
-UNIT
-----
-As default the unit is the byte, however it is possible to append a suffix
-to the arguments like 'k' for KBytes, 'm' for MBytes...
+SIZE UNITS
+----------
+The default unit is 'byte'. All size parameters accept suffixes in the 1024
+base. The recognized suffixes are: 'k', 'm', 'g', 't', 'e', both uppercase and
+lowercase.
+
+MULTIPLE DEVICES
+----------------
+
+Before mounting a multiple device filesystem, the kernel module must know the
+association of the block devices that are attached to the filesystem UUID.
+
+There is typically no action needed from the user. On a system that utilizes a
+udev-like daemon, any new block device is automatically registered. The rules
+call *btrfs device scan*.
+
+The same command can be used to trigger the device scanning if the btrfs kernel
+module is reloaded (naturally all previous information about the device
+registration is lost).
+
+Another possibility is to use the mount options *device* to specify the list of
+devices to scan at the time of mount.
+
+ # mount -o device=/dev/sdb,device=/dev/sdc /dev/sda /mnt
+
+NOTE: that this means only scanning, if the devices do not exist in the system,
+mount will fail anyway. This can happen on systems without initramfs/initrd and
+root partition created with RAID1/10/5/6 profiles. The mount action can happen
+before all block devices are discovered. The waiting is usually done on the
+initramfs/initrd systems.
+
+FILESYSTEM FEATURES
+-------------------
+
+*mixed-bg*::
+mixed data and metadata block groups, also set by option '--mixed'
+
+*extref*::
+(default since btrfs-progs 3.12, kernel support since 3.7)
++
+increased hardlink limit per file in a directory to 65536, older kernels
+supported a varying number of hardlinks depending on the sum of all file name
+sizes that can be stored into one metadata block
+
+*raid56*::
+extended format for RAID5/6, also enabled if raid5 or raid6 block groups
+are selected
+
+*skinny-metadata*::
+(default since btrfs-progs 3.18, kernel support since 3.10)
++
+reduced-size metadata for extent references, saves a few percent of metadata
+
+*no-holes*::
+improved representation of file extents where holes are not explicitly
+stored as an extent, saves a few percent of metadata if sparse files are used
+
+BLOCK GROUPS, CHUNKS, RAID
+--------------------------
+
+The highlevel organizational units of a filesystem are block groups of three types:
+data, metadata and system.
+
+*DATA*::
+store data blocks and nothing else
+
+*METADATA*::
+store internal metadata in b-trees, can store file data if they fit into the
+inline limit
+
+*SYSTEM*::
+store structures that describe the mapping between the physical devices and the
+linear logical space representing the filesystem
+
+Other terms commonly used:
+
+*block group*::
+*chunk*::
+a logical range of space of a given profile, stores data, metadata or both;
+sometimes the terms are used interchangably
++
+A typical size of metadata block group is 256MiB (filesystem smaller than
+50GiB) and 1GiB (larger than 50GiB), for data it's 1GiB. The system block group
+size is a few megabytes.
+
+*RAID*::
+a block group profile type that utilizes RAID-like features on multiple
+devices: striping, mirroring, parity
+
+*profile*::
+when used in connection with block groups refers to the allocation strategy
+and constraints, see the section 'PROFILES' for more details
+
+PROFILES
+--------
+
+There are the following block group types available:
+
+[ width="60%",options="header" ]
+|=============================================================
+| Profile | Redundancy | Striping | Min/max devices
+| single | 1 copy | n/a | 1/any
+| DUP | 2 copies / 1 device | n/a | 1/1
+| RAID0 | n/a | 1 to N | 2/any
+| RAID10 | 2 copies | 1 to N | 4/any
+| RAID5 | 2 copies | 3 to N - 1 | 2/any
+| RAID6 | 3 copies | 3 to N - 2 | 3/any
+|=============================================================
+
+KNOWN ISSUES
+------------
+
+**SMALL FILESYSTEMS AND LARGE NODESIZE**
+
+The combination of small filesystem size and large nodesize is not recommended
+in general and can lead to various ENOSPC-related issues during mount time or runtime.
+
+Since mixed block group creation is optional, we allow small
+filesystem instances with differing values for 'sectorsize' and 'nodesize'
+to be created and could end up in the following situation:
+
+ # mkfs.btrfs -f -n 65536 /dev/loop0
+ btrfs-progs v3.19-rc2-405-g976307c
+ See http://btrfs.wiki.kernel.org for more information.
+
+ Performing full device TRIM (512.00MiB) ...
+ Label: (null)
+ UUID: 49fab72e-0c8b-466b-a3ca-d1bfe56475f0
+ Node size: 65536
+ Sector size: 4096
+ Filesystem size: 512.00MiB
+ Block group profiles:
+ Data: single 8.00MiB
+ Metadata: DUP 40.00MiB
+ System: DUP 12.00MiB
+ SSD detected: no
+ Incompat features: extref, skinny-metadata
+ Number of devices: 1
+ Devices:
+ ID SIZE PATH
+ 1 512.00MiB /dev/loop0
+
+ # mount /dev/loop0 /mnt/
+ mount: mount /dev/loop0 on /mnt failed: No space left on device
+
+The ENOSPC occurs during the creation of the UUID tree. This is caused
+by large metadata blocks and space reservation strategy that allocates more
+than can fit into the filesystem.
+
AVAILABILITY
------------
-*btrfs* is part of btrfs-progs.
+*mkfs.btrfs* is part of btrfs-progs.
Please refer to the btrfs wiki http://btrfs.wiki.kernel.org for
further details.
SEE ALSO
--------
-`btrfs`(8)
+`btrfs`(8), `wipefs`(8)
diff --git a/Makefile.extrawarn b/Makefile.extrawarn
new file mode 100644
index 0000000..1f4bda9
--- /dev/null
+++ b/Makefile.extrawarn
@@ -0,0 +1,90 @@
+# From linux.git/scripts/Kbuild.include
+#
+# try-run
+# Usage: option = $(call try-run, $(CC)...-o "$$TMP",option-ok,otherwise)
+# Exit code chooses option. "$$TMP" is can be used as temporary file and
+# is automatically cleaned up.
+try-run = $(shell set -e; \
+ TMP="$(TMPOUT).$$$$.tmp"; \
+ TMPO="$(TMPOUT).$$$$.o"; \
+ if ($(1)) >/dev/null 2>&1; \
+ then echo "$(2)"; \
+ else echo "$(3)"; \
+ fi; \
+ rm -f "$$TMP" "$$TMPO")
+
+ # cc-option
+ # Usage: cflags-y += $(call cc-option,-march=winchip-c6,-march=i586)
+
+ cc-option = $(call try-run,\
+ $(CC) $(CFLAGS) $(1) -c -x c /dev/null -o "$$TMP",$(1),$(2))
+
+# From linux.git/scripts/Makefile.extrawarn
+# ==========================================================================
+#
+# make W=... settings
+#
+# W=1 - warnings that may be relevant and does not occur too often
+# W=2 - warnings that occur quite often but may still be relevant
+# W=3 - the more obscure warnings, can most likely be ignored
+#
+# $(call cc-option, -W...) handles gcc -W.. options which
+# are not supported by all versions of the compiler
+# ==========================================================================
+
+ifeq ("$(origin W)", "command line")
+ export BUILD_ENABLE_EXTRA_GCC_CHECKS := $(W)
+endif
+
+ifdef BUILD_ENABLE_EXTRA_GCC_CHECKS
+warning- := $(empty)
+
+warning-1 := -Wextra -Wunused -Wno-unused-parameter
+warning-1 += -Wmissing-declarations
+warning-1 += -Wmissing-format-attribute
+warning-1 += $(call cc-option, -Wmissing-prototypes)
+warning-1 += -Wold-style-definition
+warning-1 += $(call cc-option, -Wmissing-include-dirs)
+warning-1 += $(call cc-option, -Wunused-but-set-variable)
+warning-1 += $(call cc-disable-warning, missing-field-initializers)
+
+warning-2 := -Waggregate-return
+warning-2 += -Wcast-align
+warning-2 += -Wdisabled-optimization
+warning-2 += -Wnested-externs
+warning-2 += -Wshadow
+warning-2 += $(call cc-option, -Wlogical-op)
+warning-2 += $(call cc-option, -Wmissing-field-initializers)
+
+warning-3 := -Wbad-function-cast
+warning-3 += -Wcast-qual
+warning-3 += -Wconversion
+warning-3 += -Wpacked
+warning-3 += -Wpadded
+warning-3 += -Wpointer-arith
+warning-3 += -Wredundant-decls
+warning-3 += -Wswitch-default
+warning-3 += $(call cc-option, -Wpacked-bitfield-compat)
+warning-3 += $(call cc-option, -Wvla)
+
+warning := $(warning-$(findstring 1, $(BUILD_ENABLE_EXTRA_GCC_CHECKS)))
+warning += $(warning-$(findstring 2, $(BUILD_ENABLE_EXTRA_GCC_CHECKS)))
+warning += $(warning-$(findstring 3, $(BUILD_ENABLE_EXTRA_GCC_CHECKS)))
+
+ifeq ("$(strip $(warning))","")
+ $(error W=$(BUILD_ENABLE_EXTRA_GCC_CHECKS) is unknown)
+endif
+
+EXTRAWARN_CFLAGS += $(warning)
+else
+
+ifeq ($(COMPILER),clang)
+EXTRAWARN_CFLAGS += $(call cc-disable-warning, initializer-overrides)
+EXTRAWARN_CFLAGS += $(call cc-disable-warning, unused-value)
+EXTRAWARN_CFLAGS += $(call cc-disable-warning, format)
+EXTRAWARN_CFLAGS += $(call cc-disable-warning, unknown-warning-option)
+EXTRAWARN_CFLAGS += $(call cc-disable-warning, sign-compare)
+EXTRAWARN_CFLAGS += $(call cc-disable-warning, format-zero-length)
+EXTRAWARN_CFLAGS += $(call cc-disable-warning, uninitialized)
+endif
+endif
diff --git a/Makefile.in b/Makefile.in
index 514a76f..8e24808 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -1,6 +1,29 @@
+# btrfs-progs
+#
+# Basic build targets:
+# all all main tools
+# static build static bnaries, requires static version of the libraries
+# test run the full testsuite
+# install install to default location (/usr/local)
+# clean clean built binaries (not the documentation)
+#
+# Tuning by variables (environment or make arguments):
+# V=1 verbose, print command lines (default: quiet)
+# C=1 run checker before compilation (default checker: sparse)
+# W=123 build with warnings (default: off)
+# EXTRA_CFLAGS additional compiler flags
+# EXTRA_LDFLAGS additional linker flags
+#
+# Static checkers:
+# CHECKER static checker binary to be called (default: sparse)
+# CHECKER_FLAGS flags to pass to CHECKER, can override CFLAGS
+#
+
# Export all variables to sub-makes by default
export
+include Makefile.extrawarn
+
CC = @CC@
LN_S = @LN_S@
AR = @AR@
@@ -19,7 +42,7 @@ CFLAGS = @CFLAGS@ \
-DBTRFS_FLAT_INCLUDES \
-D_XOPEN_SOURCE=700 \
-fno-strict-aliasing \
- -fPIC $(EXTRA_CFLAGS)
+ -fPIC $(KBUILD_CFLAGS) $(EXTRA_CFLAGS)
LDFLAGS = @LDFLAGS@ \
-rdynamic $(EXTRA_LDFLAGS)
@@ -33,6 +56,14 @@ STATIC_LDFLAGS = -static -Wl,--gc-sections
STATIC_LIBS = @UUID_LIBS_STATIC@ @BLKID_LIBS_STATIC@ \
@ZLIB_LIBS_STATIC@ @LZO2_LIBS_STATIC@ -L. -pthread
+# don't use FORTIFY with sparse because glibc with FORTIFY can
+# generate so many sparse errors that sparse stops parsing,
+# which masks real errors that we want to see.
+CHECKER := sparse
+CHECKER_FLAGS := -include $(check_defs) -D__CHECKER__ \
+ -D__CHECK_ENDIAN__ -Wbitwise -Wuninitialized -Wshadow -Wundef \
+ -U_FORTIFY_SOURCE
+
objects = ctree.o disk-io.o radix-tree.o extent-tree.o print-tree.o \
root-tree.o dir-item.o file-item.o inode-item.o inode-map.o \
extent-cache.o extent_io.o volumes.o utils.o repair.o \
@@ -125,7 +156,6 @@ headers = $(libbtrfs_headers)
# make C=1 to enable sparse
check_defs := .cc-defines.h
ifdef C
- #
# We're trying to use sparse against glibc headers which go wild
# trying to use internal compiler macros to test features. We
# copy gcc's and give them to sparse. But not __SIZE_TYPE__
@@ -133,13 +163,8 @@ ifdef C
#
dummy := $(shell $(CC) -dM -E -x c - < /dev/null | \
grep -v __SIZE_TYPE__ > $(check_defs))
- check = sparse -include $(check_defs) -D__CHECKER__ \
- -D__CHECK_ENDIAN__ -Wbitwise -Wuninitialized -Wshadow -Wundef
+ check = $(CHECKER)
check_echo = echo
- # don't use FORTIFY with sparse because glibc with FORTIFY can
- # generate so many sparse errors that sparse stops parsing,
- # which masks real errors that we want to see.
- CFLAGS += -U_FORTIFY_SOURCE
else
check = true
check_echo = true
@@ -150,7 +175,7 @@ endif
.c.o:
@$(check_echo) " [SP] $<"
- $(Q)$(check) $(CFLAGS) $<
+ $(Q)$(check) $(CFLAGS) $(CHECKER_FLAGS) $<
@echo " [CC] $@"
$(Q)$(CC) $(CFLAGS) -c $<
@@ -176,11 +201,19 @@ test-misc: btrfs btrfs-image btrfs-corrupt-block btrfs-debug-tree mkfs.btrfs btr
@echo " [TEST] misc-tests.sh"
$(Q)bash tests/misc-tests.sh
+test-mkfs: btrfs mkfs.btrfs
+ @echo " [TEST] mkfs-tests.sh"
+ $(Q)bash tests/mkfs-tests.sh
+
+test-fuzz: btrfs
+ @echo " [TEST] fuzz-tests.sh"
+ $(Q)bash tests/fuzz-tests.sh
+
test-clean:
@echo "Cleaning tests"
$(Q)bash tests/clean-tests.sh
-test: test-fsck test-convert test-misc
+test: test-fsck test-mkfs test-convert test-misc test-fuzz
#
# NOTE: For static compiles, you need to have all the required libs
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..2f9d4e7
--- /dev/null
+++ b/README.md
@@ -0,0 +1,41 @@
+Btrfs-progs
+===========
+
+Userspace utilities to manage btrfs filesystems.
+License: GPLv2.
+
+Btrfs is a copy on write (COW) filesystem for Linux aimed at implementing
+advanced features while focusing on fault tolerance, repair and easy
+administration.
+
+
+This repository hosts following utilities:
+
+* **btrfs** &mdash; the main administration tool ([manual page](https://btrfs.wiki.kernel.org/index.php/Manpage/btrfs))
+* **mkfs.btrfs** &mdash; utility to create the filesystem ([manual page](https://btrfs.wiki.kernel.org/index.php/Manpage/mkfs.btrfs))
+
+See INSTALL for build instructions.
+
+Release cycle
+-------------
+
+The major version releases are time-based and follow the cycle of the linux
+kernel releases. The cycle usually takes 2 months. A minor version releases may
+happen in the meantime if there are queued bug fixes or minor useful
+improvements.
+
+Development
+-----------
+
+The patch submissions, development or general discussions take place at
+*linux-btrfs@vger.kernel.org* mailinglist, subsciption not required.
+
+References
+----------
+
+* [Wiki with more information](https://btrfs.wiki.kernel.org)
+* [Btrfs-progs changelogs](https://btrfs.wiki.kernel.org/index.php/Changelog#By_version_.28btrfs-progs.29)
+* [wiki/FAQ](https://btrfs.wiki.kernel.org/index.php/FAQ)
+* [wiki/Getting started](https://btrfs.wiki.kernel.org/index.php/Getting_started)
+* [wiki/TODO](https://btrfs.wiki.kernel.org/index.php/Project_ideas#Userspace_tools_projects)
+* [wiki/Developer's FAQ](https://btrfs.wiki.kernel.org/index.php/Developer's_FAQ)
diff --git a/backref.c b/backref.c
index 9a2efca..8f41f82 100644
--- a/backref.c
+++ b/backref.c
@@ -323,9 +323,9 @@ static int __resolve_indirect_ref(struct btrfs_fs_info *fs_info,
eb = path->nodes[level];
while (!eb) {
- WARN_ON(!level);
if (!level) {
ret = 1;
+ WARN_ON(1);
goto out;
}
level--;
@@ -1178,7 +1178,6 @@ int extent_from_logical(struct btrfs_fs_info *fs_info, u64 logical,
logical, logical - found_key->objectid, found_key->objectid,
found_key->offset, flags, item_size);
- WARN_ON(!flags_ret);
if (flags_ret) {
if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK)
*flags_ret = BTRFS_EXTENT_FLAG_TREE_BLOCK;
@@ -1187,9 +1186,10 @@ int extent_from_logical(struct btrfs_fs_info *fs_info, u64 logical,
else
BUG_ON(1);
return 0;
+ } else {
+ WARN_ON(1);
+ return -EIO;
}
-
- return -EIO;
}
/*
diff --git a/btrfs-calc-size.c b/btrfs-calc-size.c
index 7287858..b84cda9 100644
--- a/btrfs-calc-size.c
+++ b/btrfs-calc-size.c
@@ -372,8 +372,8 @@ out_print:
printf("\tTotal seeks: %Lu\n", stat.total_seeks);
printf("\t\tForward seeks: %Lu\n", stat.forward_seeks);
printf("\t\tBackward seeks: %Lu\n", stat.backward_seeks);
- printf("\t\tAvg seek len: %Lu\n", stat.total_seek_len /
- stat.total_seeks);
+ printf("\t\tAvg seek len: %llu\n", stat.total_seeks ?
+ stat.total_seek_len / stat.total_seeks : 0);
print_seek_histogram(&stat);
printf("\tTotal clusters: %Lu\n", stat.total_clusters);
printf("\t\tAvg cluster size: %Lu\n", stat.total_cluster_size /
@@ -417,7 +417,14 @@ out:
free(seek);
}
- btrfs_free_path(path);
+ /*
+ * We only use path to save node data in iterating,
+ * without holding eb's ref_cnt in path.
+ * Don't use btrfs_free_path() here, it will free these
+ * eb again, and cause many problems, as negative ref_cnt
+ * or invalid memory access.
+ */
+ free(path);
return ret;
}
@@ -508,5 +515,6 @@ int main(int argc, char **argv)
out:
close_ctree(root);
free(roots);
+ btrfs_close_all_devices();
return ret;
}
diff --git a/btrfs-convert.c b/btrfs-convert.c
index 802930c..5b9171e 100644
--- a/btrfs-convert.c
+++ b/btrfs-convert.c
@@ -2292,6 +2292,7 @@ static int do_convert(const char *devname, int datacsum, int packing, int noxatt
{
int i, ret, blocks_per_node;
int fd = -1;
+ int is_btrfs = 0;
u32 blocksize;
u64 blocks[7];
u64 total_bytes;
@@ -2459,6 +2460,7 @@ static int do_convert(const char *devname, int datacsum, int packing, int noxatt
fprintf(stderr, "unable to migrate super block\n");
goto fail;
}
+ is_btrfs = 1;
root = open_ctree_fd(fd, devname, 0, OPEN_CTREE_WRITES);
if (!root) {
@@ -2479,7 +2481,11 @@ static int do_convert(const char *devname, int datacsum, int packing, int noxatt
fail:
if (fd != -1)
close(fd);
- fprintf(stderr, "conversion aborted.\n");
+ if (is_btrfs)
+ fprintf(stderr,
+ "WARNING: an error occured during chunk mapping fixup, filesystem mountable but not finalized\n");
+ else
+ fprintf(stderr, "conversion aborted\n");
return -1;
}
@@ -2591,6 +2597,23 @@ static int do_rollback(const char *devname)
btrfs_init_path(&path);
key.objectid = CONV_IMAGE_SUBVOL_OBJECTID;
+ key.type = BTRFS_ROOT_BACKREF_KEY;
+ key.offset = BTRFS_FS_TREE_OBJECTID;
+ ret = btrfs_search_slot(NULL, root->fs_info->tree_root, &key, &path, 0,
+ 0);
+ btrfs_release_path(&path);
+ if (ret > 0) {
+ fprintf(stderr,
+ "ERROR: unable to convert ext2 image subvolume, is it deleted?\n");
+ goto fail;
+ } else if (ret < 0) {
+ fprintf(stderr,
+ "ERROR: unable to open ext2_subvol, id=%llu: %s\n",
+ (unsigned long long)key.objectid, strerror(-ret));
+ goto fail;
+ }
+
+ key.objectid = CONV_IMAGE_SUBVOL_OBJECTID;
key.type = BTRFS_ROOT_ITEM_KEY;
key.offset = (u64)-1;
image_root = btrfs_read_fs_root(root->fs_info, &key);
diff --git a/btrfs-debug-tree.c b/btrfs-debug-tree.c
index 7d8e876..8adc39f 100644
--- a/btrfs-debug-tree.c
+++ b/btrfs-debug-tree.c
@@ -28,6 +28,7 @@
#include "disk-io.h"
#include "print-tree.h"
#include "transaction.h"
+#include "volumes.h"
#include "utils.h"
static int print_usage(int ret)
@@ -428,5 +429,7 @@ no_node:
printf("uuid %s\n", uuidbuf);
printf("%s\n", PACKAGE_STRING);
close_root:
- return close_ctree(root);
+ ret = close_ctree(root);
+ btrfs_close_all_devices();
+ return ret;
}
diff --git a/btrfs-find-root.c b/btrfs-find-root.c
index 01b3603..fc3812c 100644
--- a/btrfs-find-root.c
+++ b/btrfs-find-root.c
@@ -216,5 +216,6 @@ int main(int argc, char **argv)
out:
btrfs_find_root_free(&result);
close_ctree(root);
+ btrfs_close_all_devices();
return ret;
}
diff --git a/btrfs-fragments.c b/btrfs-fragments.c
index d742f60..17768c3 100644
--- a/btrfs-fragments.c
+++ b/btrfs-fragments.c
@@ -436,11 +436,9 @@ int main(int argc, char **argv)
path = argv[optind++];
- fd = open_file_or_dir(path, &dirstream);
- if (fd < 0) {
- fprintf(stderr, "ERROR: can't access '%s'\n", path);
+ fd = btrfs_open_dir(path, &dirstream, 1);
+ if (fd < 0)
exit(1);
- }
if (flags == 0)
flags = BTRFS_BLOCK_GROUP_DATA | BTRFS_BLOCK_GROUP_METADATA;
diff --git a/btrfs-image.c b/btrfs-image.c
index 82eed05..d1d3d07 100644
--- a/btrfs-image.c
+++ b/btrfs-image.c
@@ -1505,10 +1505,9 @@ static struct extent_buffer *alloc_dummy_eb(u64 bytenr, u32 size)
{
struct extent_buffer *eb;
- eb = malloc(sizeof(struct extent_buffer) + size);
+ eb = calloc(1, sizeof(struct extent_buffer) + size);
if (!eb)
return NULL;
- memset(eb, 0, sizeof(struct extent_buffer) + size);
eb->start = bytenr;
eb->len = size;
@@ -2788,9 +2787,11 @@ int main(int argc, char *argv[])
if (compress_level > 0 || create == 0) {
if (num_threads == 0) {
- num_threads = sysconf(_SC_NPROCESSORS_ONLN);
- if (num_threads <= 0)
- num_threads = 1;
+ long tmp = sysconf(_SC_NPROCESSORS_ONLN);
+
+ if (tmp <= 0)
+ tmp = 1;
+ num_threads = tmp;
}
} else {
num_threads = 0;
diff --git a/btrfs-list.c b/btrfs-list.c
index d54de61..7529e11 100644
--- a/btrfs-list.c
+++ b/btrfs-list.c
@@ -226,13 +226,12 @@ struct btrfs_list_comparer_set *btrfs_list_alloc_comparer_set(void)
size = sizeof(struct btrfs_list_comparer_set) +
BTRFS_LIST_NCOMPS_INCREASE * sizeof(struct btrfs_list_comparer);
- set = malloc(size);
+ set = calloc(1, size);
if (!set) {
fprintf(stderr, "memory allocation failed\n");
exit(1);
}
- memset(set, 0, size);
set->total = BTRFS_LIST_NCOMPS_INCREASE;
return set;
@@ -474,12 +473,11 @@ static int add_root(struct root_lookup *root_lookup,
if (!ret)
return 0;
- ri = malloc(sizeof(*ri));
+ ri = calloc(1, sizeof(*ri));
if (!ri) {
printf("memory allocation failed\n");
exit(1);
}
- memset(ri, 0, sizeof(*ri));
ri->root_id = root_id;
if (name && name_len > 0) {
@@ -1208,13 +1206,12 @@ struct btrfs_list_filter_set *btrfs_list_alloc_filter_set(void)
size = sizeof(struct btrfs_list_filter_set) +
BTRFS_LIST_NFILTERS_INCREASE * sizeof(struct btrfs_list_filter);
- set = malloc(size);
+ set = calloc(1, size);
if (!set) {
fprintf(stderr, "memory allocation failed\n");
exit(1);
}
- memset(set, 0, size);
set->total = BTRFS_LIST_NFILTERS_INCREASE;
return set;
diff --git a/btrfs-map-logical.c b/btrfs-map-logical.c
index d9fa6b2..0161b5c 100644
--- a/btrfs-map-logical.c
+++ b/btrfs-map-logical.c
@@ -359,5 +359,6 @@ close:
close_ctree(root);
if (ret < 0)
ret = 1;
+ btrfs_close_all_devices();
return ret;
}
diff --git a/btrfs-select-super.c b/btrfs-select-super.c
index b790f3e..df74153 100644
--- a/btrfs-select-super.c
+++ b/btrfs-select-super.c
@@ -23,6 +23,7 @@
#include <sys/stat.h>
#include "kerncompat.h"
#include "ctree.h"
+#include "volumes.h"
#include "disk-io.h"
#include "print-tree.h"
#include "transaction.h"
@@ -101,5 +102,7 @@ int main(int ac, char **av)
*/
printf("using SB copy %llu, bytenr %llu\n", (unsigned long long)num,
(unsigned long long)bytenr);
+ close_ctree(root);
+ btrfs_close_all_devices();
return ret;
}
diff --git a/btrfs-show-super.c b/btrfs-show-super.c
index 27414c8..d8ad69e 100644
--- a/btrfs-show-super.c
+++ b/btrfs-show-super.c
@@ -48,6 +48,7 @@ static void print_usage(void)
fprintf(stderr, "\t-a : print information of all superblocks\n");
fprintf(stderr, "\t-i <super_mirror> : specify which mirror to print out\n");
fprintf(stderr, "\t-F : attempt to dump superblocks with bad magic\n");
+ fprintf(stderr, "\t-s <bytenr> : specify alternate superblock offset\n");
fprintf(stderr, "%s\n", PACKAGE_STRING);
}
@@ -63,7 +64,7 @@ int main(int argc, char **argv)
u64 arg;
u64 sb_bytenr = btrfs_sb_offset(0);
- while ((opt = getopt(argc, argv, "fFai:")) != -1) {
+ while ((opt = getopt(argc, argv, "fFai:s:")) != -1) {
switch (opt) {
case 'i':
arg = arg_strtou64(optarg);
@@ -86,6 +87,10 @@ int main(int argc, char **argv)
case 'F':
force = 1;
break;
+ case 's':
+ sb_bytenr = arg_strtou64(optarg);
+ all = 0;
+ break;
default:
print_usage();
exit(1);
diff --git a/btrfs.c b/btrfs.c
index 63df377..14b556b 100644
--- a/btrfs.c
+++ b/btrfs.c
@@ -18,6 +18,7 @@
#include <stdlib.h>
#include <string.h>
+#include "volumes.h"
#include "crc32c.h"
#include "commands.h"
#include "utils.h"
@@ -171,20 +172,24 @@ static int cmd_version(int argc, char **argv)
return 0;
}
-static void handle_options(int *argc, char ***argv)
+static void check_options(int argc, char **argv)
{
- if (*argc > 0) {
- const char *arg = (*argv)[0];
- if (arg[0] != '-' ||
- !strcmp(arg, "--help") ||
- !strcmp(arg, "--version"))
- return;
- fprintf(stderr, "Unknown option: %s\n", arg);
- fprintf(stderr, "usage: %s\n",
- btrfs_cmd_group.usagestr[0]);
- exit(129);
- }
- return;
+ const char *arg;
+
+ if (argc == 0)
+ return;
+
+ arg = argv[0];
+
+ if (arg[0] != '-' ||
+ !strcmp(arg, "--help") ||
+ !strcmp(arg, "--version"))
+ return;
+
+ fprintf(stderr, "Unknown option: %s\n", arg);
+ fprintf(stderr, "usage: %s\n",
+ btrfs_cmd_group.usagestr[0]);
+ exit(129);
}
static const struct cmd_group btrfs_cmd_group = {
@@ -214,6 +219,7 @@ int main(int argc, char **argv)
{
const struct cmd_struct *cmd;
const char *bname;
+ int ret;
if ((bname = strrchr(argv[0], '/')) != NULL)
bname++;
@@ -225,7 +231,7 @@ int main(int argc, char **argv)
} else {
argc--;
argv++;
- handle_options(&argc, &argv);
+ check_options(argc, argv);
if (argc > 0) {
if (!prefixcmp(argv[0], "--"))
argv[0] += 2;
@@ -242,5 +248,10 @@ int main(int argc, char **argv)
crc32c_optimization_init();
fixup_argv0(argv, cmd->token);
- exit(cmd->fn(argc, argv));
+
+ ret = cmd->fn(argc, argv);
+
+ btrfs_close_all_devices();
+
+ exit(ret);
}
diff --git a/btrfstune.c b/btrfstune.c
index c248ee6..0907aa9 100644
--- a/btrfstune.c
+++ b/btrfstune.c
@@ -548,6 +548,7 @@ int main(int argc, char *argv[])
}
out:
close_ctree(root);
+ btrfs_close_all_devices();
return ret;
}
diff --git a/chunk-recover.c b/chunk-recover.c
index 1fb04f7..f8693f7 100644
--- a/chunk-recover.c
+++ b/chunk-recover.c
@@ -85,13 +85,12 @@ static struct extent_record *btrfs_new_extent_record(struct extent_buffer *eb)
{
struct extent_record *rec;
- rec = malloc(sizeof(*rec));
+ rec = calloc(1, sizeof(*rec));
if (!rec) {
fprintf(stderr, "Fail to allocate memory for extent record.\n");
exit(1);
}
- memset(rec, 0, sizeof(*rec));
rec->cache.start = btrfs_header_bytenr(eb);
rec->cache.size = eb->len;
rec->generation = btrfs_header_generation(eb);
@@ -847,11 +846,16 @@ static int scan_devices(struct recover_control *rc)
if (!dev_scans)
return -ENOMEM;
t_scans = (pthread_t *)malloc(sizeof(pthread_t) * devnr);
- if (!t_scans)
+ if (!t_scans) {
+ free(dev_scans);
return -ENOMEM;
+ }
t_rets = (long *)malloc(sizeof(long) * devnr);
- if (!t_rets)
+ if (!t_rets) {
+ free(dev_scans);
+ free(t_scans);
return -ENOMEM;
+ }
list_for_each_entry(dev, &rc->fs_devices->devices, dev_list) {
fd = open(dev->name, O_RDONLY);
@@ -2228,10 +2232,9 @@ static int btrfs_recover_chunks(struct recover_control *rc)
nstripes = btrfs_get_device_extents(bg->objectid,
&rc->devext.no_chunk_orphans,
&devexts);
- chunk = malloc(btrfs_chunk_record_size(nstripes));
+ chunk = calloc(1, btrfs_chunk_record_size(nstripes));
if (!chunk)
return -ENOMEM;
- memset(chunk, 0, btrfs_chunk_record_size(nstripes));
INIT_LIST_HEAD(&chunk->dextents);
chunk->bg_rec = bg;
chunk->cache.start = bg->objectid;
diff --git a/cmds-balance.c b/cmds-balance.c
index 9af218b..c5be6b9 100644
--- a/cmds-balance.c
+++ b/cmds-balance.c
@@ -88,46 +88,131 @@ static int parse_u64(const char *str, u64 *result)
return 0;
}
+/*
+ * Parse range that's missing some part that can be implicit:
+ * a..b - exact range, a can be equal to b
+ * a.. - implicitly unbounded maximum (end == (u64)-1)
+ * ..b - implicitly starting at 0
+ * a - invalid; unclear semantics, use parse_u64 instead
+ *
+ * Returned values are u64, value validation and interpretation should be done
+ * by the caller.
+ */
static int parse_range(const char *range, u64 *start, u64 *end)
{
char *dots;
+ char *endptr;
+ const char *rest;
+ int skipped = 0;
dots = strstr(range, "..");
- if (dots) {
- const char *rest = dots + 2;
- int skipped = 0;
+ if (!dots)
+ return 1;
- *dots = 0;
+ rest = dots + 2;
- if (!*rest) {
- *end = (u64)-1;
- skipped++;
- } else {
- if (parse_u64(rest, end))
- return 1;
- }
- if (dots == range) {
- *start = 0;
- skipped++;
- } else {
- if (parse_u64(range, start))
- return 1;
- }
+ if (!*rest) {
+ *end = (u64)-1;
+ skipped++;
+ } else {
+ *end = strtoull(rest, &endptr, 10);
+ if (*endptr)
+ return 1;
+ }
+ if (dots == range) {
+ *start = 0;
+ skipped++;
+ } else {
+ *end = strtoull(range, &endptr, 10);
+ if (*endptr != 0 && *endptr != '.')
+ return 1;
+ }
+
+ if (*start > *end) {
+ fprintf(stderr,
+ "ERROR: range %llu..%llu doesn't make sense\n",
+ (unsigned long long)*start,
+ (unsigned long long)*end);
+ return 1;
+ }
+
+ if (skipped <= 1)
+ return 0;
+
+ return 1;
+}
+/*
+ * Parse range and check if start < end
+ */
+static int parse_range_strict(const char *range, u64 *start, u64 *end)
+{
+ if (parse_range(range, start, end) == 0) {
if (*start >= *end) {
- fprintf(stderr, "Range %llu..%llu doesn't make "
- "sense\n", (unsigned long long)*start,
+ fprintf(stderr,
+ "ERROR: range %llu..%llu not allowed\n",
+ (unsigned long long)*start,
(unsigned long long)*end);
return 1;
}
-
- if (skipped <= 1)
- return 0;
+ return 0;
}
return 1;
}
+/*
+ * Convert 64bit range to 32bit with boundary checkso
+ */
+static int range_to_u32(u64 start, u64 end, u32 *start32, u32 *end32)
+{
+ if (start > (u32)-1)
+ return 1;
+
+ if (end != (u64)-1 && end > (u32)-1)
+ return 1;
+
+ *start32 = (u32)start;
+ *end32 = (u32)end;
+
+ return 0;
+}
+
+__attribute__ ((unused))
+static int parse_range_u32(const char *range, u32 *start, u32 *end)
+{
+ u64 tmp_start;
+ u64 tmp_end;
+
+ if (parse_range(range, &tmp_start, &tmp_end))
+ return 1;
+
+ if (range_to_u32(tmp_start, tmp_end, start, end))
+ return 1;
+
+ return 0;
+}
+
+__attribute__ ((unused))
+static void print_range(u64 start, u64 end)
+{
+ if (start)
+ printf("%llu", (unsigned long long)start);
+ printf("..");
+ if (end != (u64)-1)
+ printf("%llu", (unsigned long long)end);
+}
+
+__attribute__ ((unused))
+static void print_range_u32(u32 start, u32 end)
+{
+ if (start)
+ printf("%u", start);
+ printf("..");
+ if (end != (u32)-1)
+ printf("%u", end);
+}
+
static int parse_filters(char *filters, struct btrfs_balance_args *args)
{
char *this_char;
@@ -185,7 +270,7 @@ static int parse_filters(char *filters, struct btrfs_balance_args *args)
"an argument\n");
return 1;
}
- if (parse_range(value, &args->pstart, &args->pend)) {
+ if (parse_range_strict(value, &args->pstart, &args->pend)) {
fprintf(stderr, "Invalid drange argument\n");
return 1;
}
@@ -196,7 +281,7 @@ static int parse_filters(char *filters, struct btrfs_balance_args *args)
"an argument\n");
return 1;
}
- if (parse_range(value, &args->vstart, &args->vend)) {
+ if (parse_range_strict(value, &args->vstart, &args->vend)) {
fprintf(stderr, "Invalid vrange argument\n");
return 1;
}
@@ -306,11 +391,9 @@ static int do_balance(const char *path, struct btrfs_ioctl_balance_args *args,
int e;
DIR *dirstream = NULL;
- fd = open_file_or_dir(path, &dirstream);
- if (fd < 0) {
- fprintf(stderr, "ERROR: can't access '%s'\n", path);
+ fd = btrfs_open_dir(path, &dirstream, 1);
+ if (fd < 0)
return 1;
- }
ret = ioctl(fd, BTRFS_IOC_BALANCE_V2, args);
e = errno;
@@ -503,11 +586,9 @@ static int cmd_balance_pause(int argc, char **argv)
path = argv[1];
- fd = open_file_or_dir(path, &dirstream);
- if (fd < 0) {
- fprintf(stderr, "ERROR: can't access '%s'\n", path);
+ fd = btrfs_open_dir(path, &dirstream, 1);
+ if (fd < 0)
return 1;
- }
ret = ioctl(fd, BTRFS_IOC_BALANCE_CTL, BTRFS_BALANCE_CTL_PAUSE);
e = errno;
@@ -544,11 +625,9 @@ static int cmd_balance_cancel(int argc, char **argv)
path = argv[1];
- fd = open_file_or_dir(path, &dirstream);
- if (fd < 0) {
- fprintf(stderr, "ERROR: can't access '%s'\n", path);
+ fd = btrfs_open_dir(path, &dirstream, 1);
+ if (fd < 0)
return 1;
- }
ret = ioctl(fd, BTRFS_IOC_BALANCE_CTL, BTRFS_BALANCE_CTL_CANCEL);
e = errno;
@@ -586,11 +665,9 @@ static int cmd_balance_resume(int argc, char **argv)
path = argv[1];
- fd = open_file_or_dir(path, &dirstream);
- if (fd < 0) {
- fprintf(stderr, "ERROR: can't access '%s'\n", path);
+ fd = btrfs_open_dir(path, &dirstream, 1);
+ if (fd < 0)
return 1;
- }
memset(&args, 0, sizeof(args));
args.flags |= BTRFS_BALANCE_RESUME;
@@ -679,11 +756,9 @@ static int cmd_balance_status(int argc, char **argv)
path = argv[optind];
- fd = open_file_or_dir(path, &dirstream);
- if (fd < 0) {
- fprintf(stderr, "ERROR: can't access '%s'\n", path);
+ fd = btrfs_open_dir(path, &dirstream, 1);
+ if (fd < 0)
return 2;
- }
ret = ioctl(fd, BTRFS_IOC_BALANCE_PROGRESS, &args);
e = errno;
diff --git a/cmds-check.c b/cmds-check.c
index 4225b21..fd661d9 100644
--- a/cmds-check.c
+++ b/cmds-check.c
@@ -30,6 +30,7 @@
#include "repair.h"
#include "disk-io.h"
#include "print-tree.h"
+#include "task-utils.h"
#include "transaction.h"
#include "utils.h"
#include "commands.h"
@@ -40,6 +41,20 @@
#include "backref.h"
#include "ulist.h"
+enum task_position {
+ TASK_EXTENTS,
+ TASK_FREE_SPACE,
+ TASK_FS_ROOTS,
+ TASK_NOTHING, /* have to be the last element */
+};
+
+struct task_ctx {
+ int progress_enabled;
+ enum task_position tp;
+
+ struct task_info *info;
+};
+
static u64 bytes_used = 0;
static u64 total_csum_bytes = 0;
static u64 total_btree_bytes = 0;
@@ -55,6 +70,42 @@ static int repair = 0;
static int no_holes = 0;
static int init_extent_tree = 0;
static int check_data_csum = 0;
+static struct btrfs_fs_info *global_info;
+static struct task_ctx ctx = { 0 };
+
+static void *print_status_check(void *p)
+{
+ struct task_ctx *priv = p;
+ const char work_indicator[] = { '.', 'o', 'O', 'o' };
+ uint32_t count = 0;
+ static char *task_position_string[] = {
+ "checking extents",
+ "checking free space cache",
+ "checking fs roots",
+ };
+
+ task_period_start(priv->info, 1000 /* 1s */);
+
+ if (priv->tp == TASK_NOTHING)
+ return NULL;
+
+ while (1) {
+ printf("%s [%c]\r", task_position_string[priv->tp],
+ work_indicator[count % 4]);
+ count++;
+ fflush(stdout);
+ task_period_wait(priv->info);
+ }
+ return NULL;
+}
+
+static int print_status_return(void *p)
+{
+ printf("\n");
+ fflush(stdout);
+
+ return 0;
+}
struct extent_backref {
struct list_head list;
@@ -127,6 +178,7 @@ struct extent_record {
unsigned int metadata:1;
unsigned int bad_full_backref:1;
unsigned int crossing_stripes:1;
+ unsigned int wrong_chunk_type:1;
};
struct inode_backref {
@@ -2968,8 +3020,7 @@ static struct root_backref *get_root_backref(struct root_record *rec,
return backref;
}
- backref = malloc(sizeof(*backref) + namelen + 1);
- memset(backref, 0, sizeof(*backref) + namelen + 1);
+ backref = calloc(1, sizeof(*backref) + namelen + 1);
backref->ref_root = ref_root;
backref->dir = dir;
backref->index = index;
@@ -3520,6 +3571,11 @@ static int check_fs_roots(struct btrfs_root *root,
int ret;
int err = 0;
+ if (ctx.progress_enabled) {
+ ctx.tp = TASK_FS_ROOTS;
+ task_start(ctx.info);
+ }
+
/*
* Just in case we made any changes to the extent tree that weren't
* reflected into the free space cache yet.
@@ -3596,6 +3652,8 @@ out:
if (!cache_tree_empty(&wc.shared))
fprintf(stderr, "warning line %d\n", __LINE__);
+ task_stop(ctx.info);
+
return err;
}
@@ -3749,7 +3807,8 @@ 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->crossing_stripes) {
+ !rec->bad_full_backref && !rec->crossing_stripes &&
+ !rec->wrong_chunk_type) {
remove_cache_extent(extent_cache, &rec->cache);
free_all_extent_backrefs(rec);
list_del_init(&rec->list);
@@ -4313,6 +4372,56 @@ static struct data_backref *alloc_data_backref(struct extent_record *rec,
return ref;
}
+/* Check if the type of extent matches with its chunk */
+static void check_extent_type(struct extent_record *rec)
+{
+ struct btrfs_block_group_cache *bg_cache;
+
+ bg_cache = btrfs_lookup_first_block_group(global_info, rec->start);
+ if (!bg_cache)
+ return;
+
+ /* data extent, check chunk directly*/
+ if (!rec->metadata) {
+ if (!(bg_cache->flags & BTRFS_BLOCK_GROUP_DATA))
+ rec->wrong_chunk_type = 1;
+ return;
+ }
+
+ /* metadata extent, check the obvious case first */
+ if (!(bg_cache->flags & (BTRFS_BLOCK_GROUP_SYSTEM |
+ BTRFS_BLOCK_GROUP_METADATA))) {
+ rec->wrong_chunk_type = 1;
+ return;
+ }
+
+ /*
+ * Check SYSTEM extent, as it's also marked as metadata, we can only
+ * make sure it's a SYSTEM extent by its backref
+ */
+ if (!list_empty(&rec->backrefs)) {
+ struct extent_backref *node;
+ struct tree_backref *tback;
+ u64 bg_type;
+
+ node = list_entry(rec->backrefs.next, struct extent_backref,
+ list);
+ if (node->is_data) {
+ /* tree block shouldn't have data backref */
+ rec->wrong_chunk_type = 1;
+ return;
+ }
+ tback = container_of(node, struct tree_backref, node);
+
+ if (tback->root == BTRFS_CHUNK_TREE_OBJECTID)
+ bg_type = BTRFS_BLOCK_GROUP_SYSTEM;
+ else
+ bg_type = BTRFS_BLOCK_GROUP_METADATA;
+ if (!(bg_cache->flags & bg_type))
+ rec->wrong_chunk_type = 1;
+ }
+}
+
static int add_extent_rec(struct cache_tree *extent_cache,
struct btrfs_key *parent_key, u64 parent_gen,
u64 start, u64 nr, u64 extent_item_refs,
@@ -4405,6 +4514,7 @@ static int add_extent_rec(struct cache_tree *extent_cache,
if (metadata && check_crossing_stripes(rec->start,
rec->max_size))
rec->crossing_stripes = 1;
+ check_extent_type(rec);
maybe_free_extent_rec(extent_cache, rec);
return ret;
}
@@ -4420,6 +4530,7 @@ static int add_extent_rec(struct cache_tree *extent_cache,
rec->flag_block_full_backref = -1;
rec->bad_full_backref = 0;
rec->crossing_stripes = 0;
+ rec->wrong_chunk_type = 0;
INIT_LIST_HEAD(&rec->backrefs);
INIT_LIST_HEAD(&rec->dups);
INIT_LIST_HEAD(&rec->list);
@@ -4462,6 +4573,7 @@ static int add_extent_rec(struct cache_tree *extent_cache,
if (metadata)
if (check_crossing_stripes(rec->start, rec->max_size))
rec->crossing_stripes = 1;
+ check_extent_type(rec);
return ret;
}
@@ -4509,6 +4621,7 @@ static int add_tree_backref(struct cache_tree *extent_cache, u64 bytenr,
}
back->node.found_extent_tree = 1;
}
+ check_extent_type(rec);
maybe_free_extent_rec(extent_cache, rec);
return 0;
}
@@ -4778,14 +4891,12 @@ struct chunk_record *btrfs_new_chunk_record(struct extent_buffer *leaf,
ptr = btrfs_item_ptr(leaf, slot, struct btrfs_chunk);
num_stripes = btrfs_chunk_num_stripes(leaf, ptr);
- rec = malloc(btrfs_chunk_record_size(num_stripes));
+ rec = calloc(1, btrfs_chunk_record_size(num_stripes));
if (!rec) {
fprintf(stderr, "memory allocation failed\n");
exit(-1);
}
- memset(rec, 0, btrfs_chunk_record_size(num_stripes));
-
INIT_LIST_HEAD(&rec->list);
INIT_LIST_HEAD(&rec->dextents);
rec->bg_rec = NULL;
@@ -4883,12 +4994,11 @@ btrfs_new_block_group_record(struct extent_buffer *leaf, struct btrfs_key *key,
struct btrfs_block_group_item *ptr;
struct block_group_record *rec;
- rec = malloc(sizeof(*rec));
+ rec = calloc(1, sizeof(*rec));
if (!rec) {
fprintf(stderr, "memory allocation failed\n");
exit(-1);
}
- memset(rec, 0, sizeof(*rec));
rec->cache.start = key->objectid;
rec->cache.size = key->offset;
@@ -4932,12 +5042,11 @@ btrfs_new_device_extent_record(struct extent_buffer *leaf,
struct device_extent_record *rec;
struct btrfs_dev_extent *ptr;
- rec = malloc(sizeof(*rec));
+ rec = calloc(1, sizeof(*rec));
if (!rec) {
fprintf(stderr, "memory allocation failed\n");
exit(-1);
}
- memset(rec, 0, sizeof(*rec));
rec->cache.objectid = key->objectid;
rec->cache.start = key->offset;
@@ -5269,6 +5378,11 @@ static int check_space_cache(struct btrfs_root *root)
return 0;
}
+ if (ctx.progress_enabled) {
+ ctx.tp = TASK_FREE_SPACE;
+ task_start(ctx.info);
+ }
+
while (1) {
cache = btrfs_lookup_first_block_group(root->fs_info, start);
if (!cache)
@@ -5297,6 +5411,8 @@ static int check_space_cache(struct btrfs_root *root)
}
}
+ task_stop(ctx.info);
+
return error ? -EINVAL : 0;
}
@@ -6310,8 +6426,6 @@ static int record_extent(struct btrfs_trans_handle *trans,
"start %llu len %llu parent %llu root %llu\n",
rec->start, rec->max_size, parent, tback->root);
}
- if (ret)
- goto fail;
fail:
btrfs_release_path(path);
return ret;
@@ -7520,6 +7634,14 @@ static int check_extent_refs(struct btrfs_root *root,
cur_err = 1;
}
+ if (rec->wrong_chunk_type) {
+ fprintf(stderr,
+ "bad extent [%llu, %llu), type mismatch with chunk\n",
+ rec->start, rec->start + rec->max_size);
+ err = 1;
+ cur_err = 1;
+ }
+
remove_cache_extent(extent_cache, cache);
free_all_extent_backrefs(rec);
if (!init_extent_tree && repair && (!cur_err || fixed))
@@ -7971,6 +8093,11 @@ static int check_chunks_and_extents(struct btrfs_root *root)
exit(1);
}
+ if (ctx.progress_enabled) {
+ ctx.tp = TASK_EXTENTS;
+ task_start(ctx.info);
+ }
+
again:
root1 = root->fs_info->tree_root;
level = btrfs_header_level(root1->node);
@@ -8066,13 +8193,12 @@ again:
goto out;
}
- err = check_chunks(&chunk_cache, &block_group_cache,
+ ret = check_chunks(&chunk_cache, &block_group_cache,
&dev_extent_cache, NULL, NULL, NULL, 0);
- if (err) {
- if (err == -EAGAIN)
+ if (ret) {
+ if (ret == -EAGAIN)
goto loop;
- if (!ret)
- ret = err;
+ err = ret;
}
ret = check_extent_refs(root, &extent_cache);
@@ -8082,11 +8208,12 @@ again:
goto out;
}
- err = check_devices(&dev_cache, &dev_extent_cache);
- if (err && !ret)
+ ret = check_devices(&dev_cache, &dev_extent_cache);
+ if (ret && err)
ret = err;
out:
+ task_stop(ctx.info);
if (repair) {
free_corrupt_blocks_tree(root->fs_info->corrupt_blocks);
extent_io_tree_cleanup(&excluded_extents);
@@ -9238,17 +9365,24 @@ out:
const char * const cmd_check_usage[] = {
"btrfs check [options] <device>",
- "Check an unmounted btrfs filesystem.",
+ "Check structural inegrity of a filesystem (unmounted).",
+ "Check structural inegrity of an unmounted filesystem. Verify internal",
+ "trees' consistency and item connectivity. In the repair mode try to",
+ "fix the problems found.",
+ "WARNING: the repair mode is considered dangerous",
"",
"-s|--super <superblock> use this superblock copy",
"-b|--backup use the backup root copy",
"--repair try to repair the filesystem",
+ "--readonly run in read-only mode (default)",
"--init-csum-tree create a new CRC tree",
"--init-extent-tree create a new extent tree",
"--check-data-csum verify checkums of data blocks",
- "--qgroup-report print a report on qgroup consistency",
- "--subvol-extents <subvolid> print subvolume extents and sharing state",
- "--tree-root <bytenr> use the given bytenr for the tree root",
+ "-Q|--qgroup-report print a report on qgroup consistency",
+ "-E|--subvol-extents <subvolid>",
+ " print subvolume extents and sharing state",
+ "-r|--tree-root <bytenr> use the given bytenr for the tree root",
+ "-p|--progress indicate progress",
NULL
};
@@ -9283,10 +9417,11 @@ int cmd_check(int argc, char **argv)
{ "subvol-extents", required_argument, NULL, 'E' },
{ "qgroup-report", no_argument, NULL, 'Q' },
{ "tree-root", required_argument, NULL, 'r' },
+ { "progress", no_argument, NULL, 'p' },
{ NULL, 0, NULL, 0}
};
- c = getopt_long(argc, argv, "as:br:", long_options, NULL);
+ c = getopt_long(argc, argv, "as:br:p", long_options, NULL);
if (c < 0)
break;
switch(c) {
@@ -9315,6 +9450,9 @@ int cmd_check(int argc, char **argv)
case 'r':
tree_root_bytenr = arg_strtou64(optarg);
break;
+ case 'p':
+ ctx.progress_enabled = true;
+ break;
case '?':
case 'h':
usage(cmd_check_usage);
@@ -9348,6 +9486,11 @@ int cmd_check(int argc, char **argv)
if (check_argc_exact(argc, 1))
usage(cmd_check_usage);
+ if (ctx.progress_enabled) {
+ ctx.tp = TASK_NOTHING;
+ ctx.info = task_init(print_status_check, print_status_return, &ctx);
+ }
+
/* This check is the only reason for --readonly to exist */
if (readonly && repair) {
fprintf(stderr, "Repair options are not compatible with --readonly\n");
@@ -9378,6 +9521,7 @@ int cmd_check(int argc, char **argv)
goto err_out;
}
+ global_info = info;
root = info->fs_root;
/*
@@ -9474,7 +9618,8 @@ int cmd_check(int argc, char **argv)
goto close_out;
}
- fprintf(stderr, "checking extents\n");
+ if (!ctx.progress_enabled)
+ fprintf(stderr, "checking extents\n");
ret = check_chunks_and_extents(root);
if (ret)
fprintf(stderr, "Errors found in extent allocation tree or chunk allocation\n");
@@ -9495,7 +9640,8 @@ int cmd_check(int argc, char **argv)
goto close_out;
}
- fprintf(stderr, "checking free space cache\n");
+ if (!ctx.progress_enabled)
+ fprintf(stderr, "checking free space cache\n");
ret = check_space_cache(root);
if (ret)
goto out;
@@ -9508,7 +9654,8 @@ int cmd_check(int argc, char **argv)
*/
no_holes = btrfs_fs_incompat(root->fs_info,
BTRFS_FEATURE_INCOMPAT_NO_HOLES);
- fprintf(stderr, "checking fs roots\n");
+ if (!ctx.progress_enabled)
+ fprintf(stderr, "checking fs roots\n");
ret = check_fs_roots(root, &root_cache);
if (ret)
goto out;
@@ -9588,7 +9735,9 @@ out:
free_root_recs_tree(&root_cache);
close_out:
close_ctree(root);
- btrfs_close_all_devices();
err_out:
+ if (ctx.progress_enabled)
+ task_deinit(ctx.info);
+
return ret;
}
diff --git a/cmds-device.c b/cmds-device.c
index 5f2b952..c2f3a40 100644
--- a/cmds-device.c
+++ b/cmds-device.c
@@ -53,6 +53,7 @@ static int cmd_device_add(int argc, char **argv)
DIR *dirstream = NULL;
int discard = 1;
int force = 0;
+ int last_dev;
while (1) {
int c;
@@ -77,22 +78,20 @@ static int cmd_device_add(int argc, char **argv)
}
}
- argc = argc - optind;
-
- if (check_argc_min(argc, 2))
+ if (check_argc_min(argc - optind, 2))
usage(cmd_device_add_usage);
- mntpnt = argv[optind + argc - 1];
+ last_dev = argc - 1;
+ mntpnt = argv[last_dev];
fdmnt = btrfs_open_dir(mntpnt, &dirstream, 1);
if (fdmnt < 0)
return 1;
- for (i = optind; i < optind + argc - 1; i++){
+ for (i = optind; i < last_dev; i++){
struct btrfs_ioctl_vol_args ioctl_args;
int devfd, res;
u64 dev_block_count = 0;
- int mixed = 0;
char *path;
res = test_dev_for_mkfs(argv[i], force);
@@ -109,7 +108,7 @@ static int cmd_device_add(int argc, char **argv)
}
res = btrfs_prepare_device(devfd, argv[i], 1, &dev_block_count,
- 0, &mixed, discard);
+ 0, discard);
close(devfd);
if (res) {
ret++;
@@ -139,7 +138,6 @@ static int cmd_device_add(int argc, char **argv)
error_out:
close_file_or_dir(fdmnt, dirstream);
- btrfs_close_all_devices();
return !!ret;
}
@@ -288,7 +286,6 @@ static int cmd_device_scan(int argc, char **argv)
}
out:
- btrfs_close_all_devices();
return !!ret;
}
@@ -385,18 +382,9 @@ static int cmd_device_stats(int argc, char **argv)
dev_path = argv[optind];
- fdmnt = open_path_or_dev_mnt(dev_path, &dirstream);
-
- if (fdmnt < 0) {
- if (errno == EINVAL)
- fprintf(stderr,
- "ERROR: '%s' is not a mounted btrfs device\n",
- dev_path);
- else
- fprintf(stderr, "ERROR: can't access '%s': %s\n",
- dev_path, strerror(errno));
+ fdmnt = open_path_or_dev_mnt(dev_path, &dirstream, 1);
+ if (fdmnt < 0)
return 1;
- }
ret = get_fs_info(dev_path, &fi_args, &di_args);
if (ret) {
@@ -466,7 +454,6 @@ static int cmd_device_stats(int argc, char **argv)
out:
free(di_args);
close_file_or_dir(fdmnt, dirstream);
- btrfs_close_all_devices();
return err;
}
diff --git a/cmds-fi-usage.c b/cmds-fi-usage.c
index 50d6333..4b558a8 100644
--- a/cmds-fi-usage.c
+++ b/cmds-fi-usage.c
@@ -622,9 +622,23 @@ static void _cmd_filesystem_usage_tabular(unsigned unit_mode,
u64 total_unused = 0;
struct string_table *matrix = 0;
int ncols, nrows;
+ int col;
+ int unallocated_col;
+ int spaceinfos_col;
+ const int vhdr_skip = 3; /* amount of vertical header space */
+
+ /* id, path, unallocated */
+ ncols = 3;
+ spaceinfos_col = 2;
+ /* Properly count the real space infos */
+ for (i = 0; i < sargs->total_spaces; i++) {
+ if (sargs->spaces[i].flags & BTRFS_SPACE_INFO_GLOBAL_RSV)
+ continue;
+ ncols++;
+ }
- ncols = sargs->total_spaces + 2;
- nrows = 2 + 1 + device_info_count + 1 + 2;
+ /* 2 for header, empty line, devices, ===, total, used */
+ nrows = vhdr_skip + device_info_count + 1 + 2;
matrix = table_create(ncols, nrows);
if (!matrix) {
@@ -632,33 +646,33 @@ static void _cmd_filesystem_usage_tabular(unsigned unit_mode,
return;
}
+ /*
+ * We have to skip the global block reserve everywhere as it's an
+ * artificial blockgroup
+ */
+
/* header */
- for (i = 0; i < sargs->total_spaces; i++) {
- const char *description;
+ for (i = 0, col = spaceinfos_col; i < sargs->total_spaces; i++) {
u64 flags = sargs->spaces[i].flags;
if (flags & BTRFS_SPACE_INFO_GLOBAL_RSV)
continue;
- description = btrfs_group_type_str(flags);
-
- table_printf(matrix, 1+i, 0, "<%s", description);
+ table_printf(matrix, col, 0, "<%s",
+ btrfs_group_type_str(flags));
+ table_printf(matrix, col, 1, "<%s",
+ btrfs_group_profile_str(flags));
+ col++;
}
+ unallocated_col = col;
- for (i = 0; i < sargs->total_spaces; i++) {
- const char *r_mode;
-
- u64 flags = sargs->spaces[i].flags;
- r_mode = btrfs_group_profile_str(flags);
-
- table_printf(matrix, 1+i, 1, "<%s", r_mode);
- }
-
- table_printf(matrix, 1+sargs->total_spaces, 1, "<Unallocated");
+ table_printf(matrix, 0, 1, "<Id");
+ table_printf(matrix, 1, 1, "<Path");
+ table_printf(matrix, unallocated_col, 1, "<Unallocated");
/* body */
for (i = 0; i < device_info_count; i++) {
- int k, col;
+ int k;
char *p;
u64 total_allocated = 0, unused;
@@ -669,14 +683,20 @@ static void _cmd_filesystem_usage_tabular(unsigned unit_mode,
else
p++;
- table_printf(matrix, 0, i + 3, "<%s", device_info_ptr[i].path);
+ table_printf(matrix, 0, vhdr_skip + i, ">%llu",
+ device_info_ptr[i].devid);
+ table_printf(matrix, 1, vhdr_skip + i, "<%s",
+ device_info_ptr[i].path);
- for (col = 1, k = 0 ; k < sargs->total_spaces ; k++) {
+ for (col = spaceinfos_col, k = 0; k < sargs->total_spaces; k++) {
u64 flags = sargs->spaces[k].flags;
u64 devid = device_info_ptr[i].devid;
int j;
u64 size = 0;
+ if (flags & BTRFS_SPACE_INFO_GLOBAL_RSV)
+ continue;
+
for (j = 0 ; j < chunks_info_count ; j++) {
if (chunks_info_ptr[j].type != flags )
continue;
@@ -687,10 +707,10 @@ static void _cmd_filesystem_usage_tabular(unsigned unit_mode,
}
if (size)
- table_printf(matrix, col, i+3,
+ table_printf(matrix, col, vhdr_skip+ i,
">%s", pretty_size_mode(size, unit_mode));
else
- table_printf(matrix, col, i+3, ">-");
+ table_printf(matrix, col, vhdr_skip + i, ">-");
total_allocated += size;
col++;
@@ -699,28 +719,52 @@ static void _cmd_filesystem_usage_tabular(unsigned unit_mode,
unused = get_partition_size(device_info_ptr[i].path)
- total_allocated;
- table_printf(matrix, sargs->total_spaces + 1, i + 3,
+ table_printf(matrix, unallocated_col, vhdr_skip + i,
">%s", pretty_size_mode(unused, unit_mode));
total_unused += unused;
}
- for (i = 0; i <= sargs->total_spaces; i++)
- table_printf(matrix, i + 1, device_info_count + 3, "=");
+ for (i = 0; i < spaceinfos_col; i++) {
+ table_printf(matrix, i, vhdr_skip - 1, "*-");
+ table_printf(matrix, i, vhdr_skip + device_info_count, "*-");
+ }
+
+ for (i = 0, col = spaceinfos_col; i < sargs->total_spaces; i++) {
+ if (sargs->spaces[i].flags & BTRFS_SPACE_INFO_GLOBAL_RSV)
+ continue;
+
+ table_printf(matrix, col, vhdr_skip - 1, "*-");
+ table_printf(matrix, col, vhdr_skip + device_info_count, "*-");
+ col++;
+ }
+ /* One for Unallocated */
+ table_printf(matrix, col, vhdr_skip - 1, "*-");
+ table_printf(matrix, col, vhdr_skip + device_info_count, "*-");
/* footer */
- table_printf(matrix, 0, device_info_count + 4, "<Total");
- for (i = 0; i < sargs->total_spaces; i++)
- table_printf(matrix, 1 + i, device_info_count + 4, ">%s",
+ table_printf(matrix, 1, vhdr_skip + device_info_count + 1, "<Total");
+ for (i = 0, col = spaceinfos_col; i < sargs->total_spaces; i++) {
+ if (sargs->spaces[i].flags & BTRFS_SPACE_INFO_GLOBAL_RSV)
+ continue;
+
+ table_printf(matrix, col++, vhdr_skip + device_info_count + 1,
+ ">%s",
pretty_size_mode(sargs->spaces[i].total_bytes, unit_mode));
+ }
- table_printf(matrix, sargs->total_spaces + 1, device_info_count + 4,
+ table_printf(matrix, unallocated_col, vhdr_skip + device_info_count + 1,
">%s", pretty_size_mode(total_unused, unit_mode));
- table_printf(matrix, 0, device_info_count + 5, "<Used");
- for (i = 0; i < sargs->total_spaces; i++)
- table_printf(matrix, 1 + i, device_info_count+5, ">%s",
+ table_printf(matrix, 1, vhdr_skip + device_info_count + 2, "<Used");
+ for (i = 0, col = spaceinfos_col; i < sargs->total_spaces; i++) {
+ if (sargs->spaces[i].flags & BTRFS_SPACE_INFO_GLOBAL_RSV)
+ continue;
+
+ table_printf(matrix, col++, vhdr_skip + device_info_count + 2,
+ ">%s",
pretty_size_mode(sargs->spaces[i].used_bytes, unit_mode));
+ }
table_dump(matrix);
table_free(matrix);
@@ -901,10 +945,8 @@ int cmd_filesystem_usage(int argc, char **argv)
int chunkcount = 0;
int devcount = 0;
- 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[i]);
ret = 1;
goto out;
}
diff --git a/cmds-filesystem.c b/cmds-filesystem.c
index 02def40..819daa1 100644
--- a/cmds-filesystem.c
+++ b/cmds-filesystem.c
@@ -205,11 +205,10 @@ static int cmd_filesystem_df(int argc, char **argv)
path = argv[1];
- fd = open_file_or_dir(path, &dirstream);
- if (fd < 0) {
- fprintf(stderr, "ERROR: can't access '%s'\n", path);
+ fd = btrfs_open_dir(path, &dirstream, 1);
+ if (fd < 0)
return 1;
- }
+
ret = get_df(fd, &sargs);
if (ret == 0) {
@@ -468,6 +467,10 @@ static int btrfs_scan_kernel(void *search, unsigned unit_mode)
goto out;
}
+ /* skip all fs already shown as mounted fs */
+ if (is_seen_fsid(fs_info_arg.fsid))
+ continue;
+
ret = get_label_mounted(mnt->mnt_dir, label);
/* provide backward kernel compatibility */
if (ret == -ENOTTY)
@@ -681,7 +684,7 @@ static int search_umounted_fs_uuids(struct list_head *all_uuids,
if (is_seen_fsid(cur_fs->fsid))
continue;
- fs_copy = malloc(sizeof(*fs_copy));
+ fs_copy = calloc(1, sizeof(*fs_copy));
if (!fs_copy) {
ret = -ENOMEM;
goto out;
@@ -935,11 +938,9 @@ static int cmd_filesystem_sync(int argc, char **argv)
path = argv[1];
- fd = open_file_or_dir(path, &dirstream);
- if (fd < 0) {
- fprintf(stderr, "ERROR: can't access '%s'\n", path);
+ fd = btrfs_open_dir(path, &dirstream, 1);
+ if (fd < 0)
return 1;
- }
printf("FSSync '%s'\n", path);
res = ioctl(fd, BTRFS_IOC_SYNC);
@@ -1225,11 +1226,9 @@ static int cmd_filesystem_resize(int argc, char **argv)
return 1;
}
- fd = open_file_or_dir(path, &dirstream);
- if (fd < 0) {
- fprintf(stderr, "ERROR: can't access '%s'\n", path);
+ fd = btrfs_open_dir(path, &dirstream, 1);
+ if (fd < 0)
return 1;
- }
printf("Resize '%s' of '%s'\n", path, amount);
memset(&args, 0, sizeof(args));
diff --git a/cmds-inspect.c b/cmds-inspect.c
index fc3db99..40ab49b 100644
--- a/cmds-inspect.c
+++ b/cmds-inspect.c
@@ -116,11 +116,9 @@ static int cmd_inspect_inode_resolve(int argc, char **argv)
if (check_argc_exact(argc - optind, 2))
usage(cmd_inspect_inode_resolve_usage);
- fd = open_file_or_dir(argv[optind+1], &dirstream);
- if (fd < 0) {
- fprintf(stderr, "ERROR: can't access '%s'\n", argv[optind+1]);
+ fd = btrfs_open_dir(argv[optind + 1], &dirstream, 1);
+ if (fd < 0)
return 1;
- }
ret = __ino_to_path_fd(arg_strtou64(argv[optind]), fd, verbose,
argv[optind+1]);
@@ -189,9 +187,8 @@ static int cmd_inspect_logical_resolve(int argc, char **argv)
loi.size = size;
loi.inodes = ptr_to_u64(inodes);
- fd = open_file_or_dir(argv[optind+1], &dirstream);
+ fd = btrfs_open_dir(argv[optind + 1], &dirstream, 1);
if (fd < 0) {
- fprintf(stderr, "ERROR: can't access '%s'\n", argv[optind+1]);
ret = 12;
goto out;
}
@@ -239,10 +236,9 @@ static int cmd_inspect_logical_resolve(int argc, char **argv)
name);
BUG_ON(ret >= bytes_left);
free(name);
- path_fd = open_file_or_dir(full_path, &dirs);
+ path_fd = btrfs_open_dir(full_path, &dirs, 1);
if (path_fd < 0) {
- fprintf(stderr, "ERROR: can't access "
- "'%s'\n", full_path);
+ ret = -ENOENT;
goto out;
}
}
@@ -278,9 +274,8 @@ static int cmd_inspect_subvolid_resolve(int argc, char **argv)
if (check_argc_exact(argc, 3))
usage(cmd_inspect_subvolid_resolve_usage);
- fd = open_file_or_dir(argv[2], &dirstream);
+ fd = btrfs_open_dir(argv[2], &dirstream, 1);
if (fd < 0) {
- fprintf(stderr, "ERROR: can't access '%s'\n", argv[2]);
ret = -ENOENT;
goto out;
}
@@ -319,9 +314,8 @@ static int cmd_inspect_rootid(int argc, char **argv)
if (check_argc_exact(argc, 2))
usage(cmd_inspect_rootid_usage);
- fd = open_file_or_dir(argv[1], &dirstream);
+ fd = btrfs_open_dir(argv[1], &dirstream, 1);
if (fd < 0) {
- fprintf(stderr, "ERROR: can't access '%s'\n", argv[1]);
ret = -ENOENT;
goto out;
}
@@ -618,17 +612,15 @@ static int cmd_inspect_min_dev_size(int argc, char **argv)
if (check_argc_exact(argc - optind, 1))
usage(cmd_inspect_min_dev_size_usage);
- fd = open_file_or_dir(argv[optind], &dirstream);
+ fd = btrfs_open_dir(argv[optind], &dirstream, 1);
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);
-
+out:
return !!ret;
}
diff --git a/cmds-qgroup.c b/cmds-qgroup.c
index 48c1733..a64b716 100644
--- a/cmds-qgroup.c
+++ b/cmds-qgroup.c
@@ -79,11 +79,9 @@ static int qgroup_assign(int assign, int argc, char **argv)
fprintf(stderr, "ERROR: bad relation requested '%s'\n", path);
return 1;
}
- fd = open_file_or_dir(path, &dirstream);
- if (fd < 0) {
- fprintf(stderr, "ERROR: can't access '%s'\n", path);
+ fd = btrfs_open_dir(path, &dirstream, 1);
+ if (fd < 0)
return 1;
- }
ret = ioctl(fd, BTRFS_IOC_QGROUP_ASSIGN, &args);
e = errno;
@@ -137,11 +135,9 @@ static int qgroup_create(int create, int argc, char **argv)
args.create = create;
args.qgroupid = parse_qgroupid(argv[1]);
- fd = open_file_or_dir(path, &dirstream);
- if (fd < 0) {
- fprintf(stderr, "ERROR: can't access '%s'\n", path);
+ fd = btrfs_open_dir(path, &dirstream, 1);
+ if (fd < 0)
return 1;
- }
ret = ioctl(fd, BTRFS_IOC_QGROUP_CREATE, &args);
e = errno;
@@ -351,9 +347,10 @@ static int cmd_qgroup_show(int argc, char **argv)
usage(cmd_qgroup_show_usage);
path = argv[optind];
- fd = open_file_or_dir(path, &dirstream);
+ fd = btrfs_open_dir(path, &dirstream, 1);
if (fd < 0) {
- fprintf(stderr, "ERROR: can't access '%s'\n", path);
+ btrfs_qgroup_free_filter_set(filter_set);
+ btrfs_qgroup_free_comparer_set(comparer_set);
return 1;
}
@@ -460,11 +457,9 @@ static int cmd_qgroup_limit(int argc, char **argv)
} else
usage(cmd_qgroup_limit_usage);
- fd = open_file_or_dir(path, &dirstream);
- if (fd < 0) {
- fprintf(stderr, "ERROR: can't access '%s'\n", path);
+ fd = btrfs_open_dir(path, &dirstream, 1);
+ if (fd < 0)
return 1;
- }
ret = ioctl(fd, BTRFS_IOC_QGROUP_LIMIT, &args);
e = errno;
diff --git a/cmds-quota.c b/cmds-quota.c
index 8adc1bf..efbc3ef 100644
--- a/cmds-quota.c
+++ b/cmds-quota.c
@@ -45,11 +45,9 @@ static int quota_ctl(int cmd, int argc, char **argv)
memset(&args, 0, sizeof(args));
args.cmd = cmd;
- fd = open_file_or_dir(path, &dirstream);
- if (fd < 0) {
- fprintf(stderr, "ERROR: can't access '%s'\n", path);
+ fd = btrfs_open_dir(path, &dirstream, 1);
+ if (fd < 0)
return 1;
- }
ret = ioctl(fd, BTRFS_IOC_QUOTA_CTL, &args);
e = errno;
@@ -141,11 +139,9 @@ static int cmd_quota_rescan(int argc, char **argv)
memset(&args, 0, sizeof(args));
path = argv[optind];
- fd = open_file_or_dir(path, &dirstream);
- if (fd < 0) {
- fprintf(stderr, "ERROR: can't access '%s'\n", path);
+ fd = btrfs_open_dir(path, &dirstream, 1);
+ if (fd < 0)
return 1;
- }
ret = ioctl(fd, ioctlnum, &args);
e = errno;
diff --git a/cmds-replace.c b/cmds-replace.c
index 9ab8438..9ba256d 100644
--- a/cmds-replace.c
+++ b/cmds-replace.c
@@ -65,17 +65,6 @@ static const char * const replace_cmd_group_usage[] = {
NULL
};
-static int is_numerical(const char *str)
-{
- if (!(*str >= '0' && *str <= '9'))
- return 0;
- while (*str >= '0' && *str <= '9')
- str++;
- if (*str != '\0')
- return 0;
- return 1;
-}
-
static int dev_replace_cancel_fd = -1;
static void dev_replace_sigint_handler(int signal)
{
@@ -140,7 +129,6 @@ static int cmd_replace_start(int argc, char **argv)
int force_using_targetdev = 0;
u64 dstdev_block_count;
int do_not_background = 0;
- int mixed = 0;
DIR *dirstream = NULL;
u64 srcdev_size;
u64 dstdev_size;
@@ -170,18 +158,9 @@ static int cmd_replace_start(int argc, char **argv)
usage(cmd_replace_start_usage);
path = argv[optind + 2];
- fdmnt = open_path_or_dev_mnt(path, &dirstream);
-
- if (fdmnt < 0) {
- if (errno == EINVAL)
- fprintf(stderr,
- "ERROR: '%s' is not a mounted btrfs device\n",
- path);
- else
- fprintf(stderr, "ERROR: can't access '%s': %s\n",
- path, strerror(errno));
+ fdmnt = open_path_or_dev_mnt(path, &dirstream, 1);
+ if (fdmnt < 0)
goto leave_with_error;
- }
/* check for possible errors before backgrounding */
status_args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_STATUS;
@@ -223,7 +202,7 @@ static int cmd_replace_start(int argc, char **argv)
goto leave_with_error;
}
- if (is_numerical(srcdev)) {
+ if (string_is_numerical(srcdev)) {
struct btrfs_ioctl_fs_info_args fi_args;
struct btrfs_ioctl_dev_info_args *di_args = NULL;
@@ -281,7 +260,7 @@ static int cmd_replace_start(int argc, char **argv)
strncpy((char *)start_args.start.tgtdev_name, dstdev,
BTRFS_DEVICE_PATH_NAME_MAX);
ret = btrfs_prepare_device(fddstdev, dstdev, 1, &dstdev_block_count, 0,
- &mixed, 0);
+ 0);
if (ret)
goto leave_with_error;
@@ -330,7 +309,6 @@ static int cmd_replace_start(int argc, char **argv)
}
}
close_file_or_dir(fdmnt, dirstream);
- btrfs_close_all_devices();
return 0;
leave_with_error:
@@ -340,7 +318,6 @@ leave_with_error:
close(fdmnt);
if (fddstdev != -1)
close(fddstdev);
- btrfs_close_all_devices();
return 1;
}
@@ -357,7 +334,6 @@ static const char *const cmd_replace_status_usage[] = {
static int cmd_replace_status(int argc, char **argv)
{
int fd;
- int e;
int c;
char *path;
int once = 0;
@@ -379,13 +355,9 @@ static int cmd_replace_status(int argc, char **argv)
usage(cmd_replace_status_usage);
path = argv[optind];
- fd = open_file_or_dir(path, &dirstream);
- e = errno;
- if (fd < 0) {
- fprintf(stderr, "ERROR: can't access \"%s\": %s\n",
- path, strerror(e));
+ fd = btrfs_open_dir(path, &dirstream, 1);
+ if (fd < 0)
return 1;
- }
ret = print_replace_status(fd, path, once);
close_file_or_dir(fd, dirstream);
@@ -550,12 +522,9 @@ static int cmd_replace_cancel(int argc, char **argv)
usage(cmd_replace_cancel_usage);
path = argv[optind];
- fd = open_file_or_dir(path, &dirstream);
- if (fd < 0) {
- fprintf(stderr, "ERROR: can't access \"%s\": %s\n",
- path, strerror(errno));
+ fd = btrfs_open_dir(path, &dirstream, 1);
+ if (fd < 0)
return 1;
- }
args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_CANCEL;
args.result = BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT;
diff --git a/cmds-restore.c b/cmds-restore.c
index 8fc8b2a..a1445d4 100644
--- a/cmds-restore.c
+++ b/cmds-restore.c
@@ -863,7 +863,7 @@ static int copy_symlink(struct btrfs_root *root, struct btrfs_key *key,
len = btrfs_file_extent_inline_item_len(leaf,
btrfs_item_nr(path->slots[0]));
- if (len > PATH_MAX) {
+ if (len >= PATH_MAX) {
fprintf(stderr, "Symlink '%s' target length %d is longer than PATH_MAX\n",
fs_name, len);
ret = -1;
diff --git a/cmds-scrub.c b/cmds-scrub.c
index 3ebda9d..da614f2 100644
--- a/cmds-scrub.c
+++ b/cmds-scrub.c
@@ -128,11 +128,6 @@ static void print_scrub_full(struct btrfs_scrub_progress *sp)
printf("\tlast_physical: %lld\n", sp->last_physical);
}
-#define ERR(test, ...) do { \
- if (test) \
- fprintf(stderr, __VA_ARGS__); \
-} while (0)
-
#define PRINT_SCRUB_ERROR(test, desc) do { \
if (test) \
printf(" %s=%llu", desc, test); \
@@ -461,8 +456,8 @@ static int scrub_kvread(int *i, int len, int avail, const char *buf,
#define _SCRUB_INVALID do { \
if (report_errors) \
- fprintf(stderr, "WARNING: invalid data in line %d pos " \
- "%d state %d (near \"%.*s\") at %s:%d\n", \
+ warning("invalid data on line %d pos " \
+ "%d state %d (near \"%.*s\") at %s:%d", \
lineno, i, state, 20 > avail ? avail : 20, \
l + i, __FILE__, __LINE__); \
goto skip; \
@@ -848,8 +843,7 @@ static void *scrub_one_dev(void *ctx)
IOPRIO_PRIO_VALUE(sp->ioprio_class,
sp->ioprio_classdata));
if (ret)
- fprintf(stderr,
- "WARNING: setting ioprio failed: %s (ignored).\n",
+ warning("setting ioprio failed: %s (ignored)",
strerror(errno));
ret = ioctl(sp->fd, BTRFS_IOC_SCRUB, &sp->scrub_args);
@@ -1195,37 +1189,29 @@ static int scrub_start(int argc, char **argv, int resume)
do_print = 0;
if (mkdir_p(datafile)) {
- ERR(!do_quiet, "WARNING: cannot create scrub data "
- "file, mkdir %s failed: %s. Status recording "
- "disabled\n", datafile, strerror(errno));
+ warning_on(!do_quiet,
+ "cannot create scrub data file, mkdir %s failed: %s. Status recording disabled",
+ datafile, strerror(errno));
do_record = 0;
}
free(datafile);
path = argv[optind];
- fdmnt = open_path_or_dev_mnt(path, &dirstream);
-
- if (fdmnt < 0) {
- if (errno == EINVAL)
- ERR(!do_quiet,
- "ERROR: '%s' is not a mounted btrfs device\n",
- path);
- else
- ERR(!do_quiet, "ERROR: can't access '%s': %s\n",
- path, strerror(errno));
+ fdmnt = open_path_or_dev_mnt(path, &dirstream, !do_quiet);
+ if (fdmnt < 0)
return 1;
- }
ret = get_fs_info(path, &fi_args, &di_args);
if (ret) {
- ERR(!do_quiet, "ERROR: getting dev info for scrub failed: "
- "%s\n", strerror(-ret));
+ error_on(!do_quiet,
+ "getting dev info for scrub failed: %s",
+ strerror(-ret));
err = 1;
goto out;
}
if (!fi_args.num_devices) {
- ERR(!do_quiet, "ERROR: no devices found\n");
+ error_on(!do_quiet, "no devices found");
err = 1;
goto out;
}
@@ -1233,13 +1219,13 @@ static int scrub_start(int argc, char **argv, int resume)
uuid_unparse(fi_args.fsid, fsid);
fdres = scrub_open_file_r(SCRUB_DATA_FILE, fsid);
if (fdres < 0 && fdres != -ENOENT) {
- ERR(!do_quiet, "WARNING: failed to open status file: "
- "%s\n", strerror(-fdres));
+ warning_on(!do_quiet, "failed to open status file: %s",
+ strerror(-fdres));
} else if (fdres >= 0) {
past_scrubs = scrub_read_file(fdres, !do_quiet);
if (IS_ERR(past_scrubs))
- ERR(!do_quiet, "WARNING: failed to read status file: "
- "%s\n", strerror(-PTR_ERR(past_scrubs)));
+ warning_on(!do_quiet, "failed to read status file: %s",
+ strerror(-PTR_ERR(past_scrubs)));
close(fdres);
}
@@ -1262,11 +1248,11 @@ static int scrub_start(int argc, char **argv, int resume)
* single devices, there is no reason to prevent this.
*/
if (!force && is_scrub_running_on_fs(&fi_args, di_args, past_scrubs)) {
- ERR(!do_quiet,
- "ERROR: scrub is already running.\n"
- "To cancel use 'btrfs scrub cancel %s'.\n"
- "To see the status use 'btrfs scrub status [-d] %s'.\n",
- path, path);
+ error_on(!do_quiet,
+ "Scrub is already running.\n"
+ "To cancel use 'btrfs scrub cancel %s'.\n"
+ "To see the status use 'btrfs scrub status [-d] %s'",
+ path, path);
err = 1;
goto out;
}
@@ -1276,7 +1262,7 @@ static int scrub_start(int argc, char **argv, int resume)
spc.progress = calloc(fi_args.num_devices * 2, sizeof(*spc.progress));
if (!t_devs || !sp || !spc.progress) {
- ERR(!do_quiet, "ERROR: scrub failed: %s", strerror(errno));
+ error_on(!do_quiet, "scrub failed: %s", strerror(errno));
err = 1;
goto out;
}
@@ -1285,8 +1271,8 @@ static int scrub_start(int argc, char **argv, int resume)
devid = di_args[i].devid;
ret = pthread_mutex_init(&sp[i].progress_mutex, NULL);
if (ret) {
- ERR(!do_quiet, "ERROR: pthread_mutex_init failed: "
- "%s\n", strerror(ret));
+ error_on(!do_quiet, "pthread_mutex_init failed: %s",
+ strerror(ret));
err = 1;
goto out;
}
@@ -1340,7 +1326,7 @@ static int scrub_start(int argc, char **argv, int resume)
ret = connect(prg_fd, (struct sockaddr *)&addr, sizeof(addr));
if (!ret || errno != ECONNREFUSED) {
/* ... yes, so scrub must be running. error out */
- fprintf(stderr, "ERROR: scrub already running\n");
+ error("scrub already running");
close(prg_fd);
prg_fd = -1;
goto out;
@@ -1354,10 +1340,10 @@ static int scrub_start(int argc, char **argv, int resume)
if (ret != -1)
ret = listen(prg_fd, 100);
if (ret == -1) {
- ERR(!do_quiet, "WARNING: failed to open the progress status "
- "socket at %s: %s. Progress cannot be queried\n",
- sock_path[0] ? sock_path : SCRUB_PROGRESS_SOCKET_PATH,
- strerror(errno));
+ warning_on(!do_quiet,
+ "failed to open the progress status socket at %s: %s. Progress cannot be queried",
+ sock_path[0] ? sock_path :
+ SCRUB_PROGRESS_SOCKET_PATH, strerror(errno));
if (prg_fd != -1) {
close(prg_fd);
prg_fd = -1;
@@ -1371,9 +1357,9 @@ static int scrub_start(int argc, char **argv, int resume)
ret = scrub_write_progress(&spc_write_mutex, fsid, sp,
fi_args.num_devices);
if (ret) {
- ERR(!do_quiet, "WARNING: failed to write the progress "
- "status file: %s. Status recording disabled\n",
- strerror(-ret));
+ warning_on(!do_quiet,
+ "failed to write the progress status file: %s. Status recording disabled",
+ strerror(-ret));
do_record = 0;
}
}
@@ -1381,8 +1367,8 @@ static int scrub_start(int argc, char **argv, int resume)
if (do_background) {
pid = fork();
if (pid == -1) {
- ERR(!do_quiet, "ERROR: cannot scrub, fork failed: "
- "%s\n", strerror(errno));
+ error_on(!do_quiet, "cannot scrub, fork failed: %s",
+ strerror(errno));
err = 1;
goto out;
}
@@ -1400,13 +1386,13 @@ static int scrub_start(int argc, char **argv, int resume)
}
ret = wait(&stat);
if (ret != pid) {
- ERR(!do_quiet, "ERROR: wait failed: (ret=%d) "
- "%s\n", ret, strerror(errno));
+ error_on(!do_quiet, "wait failed (ret=%d): %s",
+ ret, strerror(errno));
err = 1;
goto out;
}
if (!WIFEXITED(stat) || WEXITSTATUS(stat)) {
- ERR(!do_quiet, "ERROR: scrub process failed\n");
+ error_on(!do_quiet, "scrub process failed");
err = WIFEXITED(stat) ? WEXITSTATUS(stat) : -1;
goto out;
}
@@ -1432,9 +1418,8 @@ static int scrub_start(int argc, char **argv, int resume)
scrub_one_dev, &sp[i]);
if (ret) {
if (do_print)
- fprintf(stderr, "ERROR: creating "
- "scrub_one_dev[%llu] thread failed: "
- "%s\n", devid, strerror(ret));
+ error("creating scrub_one_dev[%llu] thread failed: %s",
+ devid, strerror(ret));
err = 1;
goto out;
}
@@ -1449,8 +1434,8 @@ static int scrub_start(int argc, char **argv, int resume)
ret = pthread_create(&t_prog, NULL, scrub_progress_cycle, &spc);
if (ret) {
if (do_print)
- fprintf(stderr, "ERROR: creating progress thread "
- "failed: %s\n", strerror(ret));
+ error("creating progress thread failed: %s",
+ strerror(ret));
err = 1;
goto out;
}
@@ -1463,27 +1448,30 @@ static int scrub_start(int argc, char **argv, int resume)
ret = pthread_join(t_devs[i], NULL);
if (ret) {
if (do_print)
- fprintf(stderr, "ERROR: pthread_join failed "
- "for scrub_one_dev[%llu]: %s\n", devid,
- strerror(ret));
+ error("pthread_join failed for scrub_one_dev[%llu]: %s",
+ devid, strerror(ret));
++err;
continue;
}
- if (sp[i].ret && sp[i].ioctl_errno == ENODEV) {
- if (do_print)
- fprintf(stderr, "WARNING: device %lld not "
- "present\n", devid);
- continue;
- }
- if (sp[i].ret && sp[i].ioctl_errno == ECANCELED) {
- ++err;
- } else if (sp[i].ret) {
- if (do_print)
- fprintf(stderr, "ERROR: scrubbing %s failed "
- "for device id %lld (%s)\n", path,
- devid, strerror(sp[i].ioctl_errno));
- ++err;
- continue;
+ if (sp[i].ret) {
+ switch (sp[i].ioctl_errno) {
+ case ENODEV:
+ if (do_print)
+ warning("device %lld not present",
+ devid);
+ continue;
+ case ECANCELED:
+ ++err;
+ break;
+ default:
+ if (do_print)
+ error("scrubbing %s failed for device id %lld: ret=%d, errno=%d (%s)",
+ path, devid,
+ sp[i].ret, sp[i].ioctl_errno,
+ strerror(sp[i].ioctl_errno));
+ ++err;
+ continue;
+ }
}
if (sp[i].scrub_args.progress.uncorrectable_errors > 0)
e_uncorrectable++;
@@ -1522,23 +1510,21 @@ static int scrub_start(int argc, char **argv, int resume)
/* check for errors from the handling of the progress thread */
if (do_print && ret) {
- fprintf(stderr, "ERROR: progress thread handling failed: %s\n",
+ error("progress thread handling failed: %s",
strerror(ret));
}
/* check for errors returned from the progress thread itself */
- if (do_print && terr && terr != PTHREAD_CANCELED) {
- fprintf(stderr, "ERROR: recording progress "
- "failed: %s\n", strerror(-PTR_ERR(terr)));
- }
+ if (do_print && terr && terr != PTHREAD_CANCELED)
+ error("recording progress failed: %s",
+ strerror(-PTR_ERR(terr)));
if (do_record) {
ret = scrub_write_progress(&spc_write_mutex, fsid, sp,
fi_args.num_devices);
- if (ret && do_print) {
- fprintf(stderr, "ERROR: failed to record the result: "
- "%s\n", strerror(-ret));
- }
+ if (ret && do_print)
+ error("failed to record the result: %s",
+ strerror(-ret));
}
scrub_handle_sigint_child(-1);
@@ -1561,11 +1547,12 @@ out:
if (nothing_to_resume)
return 2;
if (e_uncorrectable) {
- ERR(!do_quiet, "ERROR: There are uncorrectable errors.\n");
+ error_on(!do_quiet, "there are uncorrectable errors");
return 3;
}
if (e_correctable)
- ERR(!do_quiet, "WARNING: errors detected during scrubbing, corrected.\n");
+ warning_on(!do_quiet,
+ "errors detected during scrubbing, corrected");
return 0;
}
@@ -1609,15 +1596,8 @@ static int cmd_scrub_cancel(int argc, char **argv)
path = argv[1];
- fdmnt = open_path_or_dev_mnt(path, &dirstream);
+ fdmnt = open_path_or_dev_mnt(path, &dirstream, 1);
if (fdmnt < 0) {
- if (errno == EINVAL)
- fprintf(stderr,
- "ERROR: '%s' is not a mounted btrfs device\n",
- path);
- else
- fprintf(stderr, "ERROR: can't access '%s': %s\n",
- path, strerror(errno));
ret = 1;
goto out;
}
@@ -1625,7 +1605,7 @@ static int cmd_scrub_cancel(int argc, char **argv)
ret = ioctl(fdmnt, BTRFS_IOC_SCRUB_CANCEL, NULL);
if (ret < 0) {
- fprintf(stderr, "ERROR: scrub cancel failed on %s: %s\n", path,
+ error("scrub cancel failed on %s: %s", path,
errno == ENOTCONN ? "not running" : strerror(errno));
if (errno == ENOTCONN)
ret = 2;
@@ -1713,28 +1693,19 @@ static int cmd_scrub_status(int argc, char **argv)
path = argv[optind];
- fdmnt = open_path_or_dev_mnt(path, &dirstream);
-
- if (fdmnt < 0) {
- if (errno == EINVAL)
- fprintf(stderr,
- "ERROR: '%s' is not a mounted btrfs device\n",
- path);
- else
- fprintf(stderr, "ERROR: can't access '%s': %s\n",
- path, strerror(errno));
+ fdmnt = open_path_or_dev_mnt(path, &dirstream, 1);
+ if (fdmnt < 0)
return 1;
- }
ret = get_fs_info(path, &fi_args, &di_args);
if (ret) {
- fprintf(stderr, "ERROR: getting dev info for scrub failed: "
- "%s\n", strerror(-ret));
+ error("getting dev info for scrub failed: %s",
+ strerror(-ret));
err = 1;
goto out;
}
if (!fi_args.num_devices) {
- fprintf(stderr, "ERROR: no devices found\n");
+ error("no devices found");
err = 1;
goto out;
}
@@ -1743,8 +1714,7 @@ static int cmd_scrub_status(int argc, char **argv)
fdres = socket(AF_UNIX, SOCK_STREAM, 0);
if (fdres == -1) {
- fprintf(stderr, "ERROR: failed to create socket to "
- "receive progress information: %s\n",
+ error("failed to create socket to receive progress information: %s",
strerror(errno));
err = 1;
goto out;
@@ -1758,8 +1728,8 @@ static int cmd_scrub_status(int argc, char **argv)
close(fdres);
fdres = scrub_open_file_r(SCRUB_DATA_FILE, fsid);
if (fdres < 0 && fdres != -ENOENT) {
- fprintf(stderr, "WARNING: failed to open status file: "
- "%s\n", strerror(-fdres));
+ warning("failed to open status file: %s",
+ strerror(-fdres));
err = 1;
goto out;
}
@@ -1768,7 +1738,7 @@ static int cmd_scrub_status(int argc, char **argv)
if (fdres >= 0) {
past_scrubs = scrub_read_file(fdres, 1);
if (IS_ERR(past_scrubs))
- fprintf(stderr, "WARNING: failed to read status: %s\n",
+ warning("failed to read status: %s",
strerror(-PTR_ERR(past_scrubs)));
}
in_progress = is_scrub_running_in_kernel(fdmnt, di_args, fi_args.num_devices);
diff --git a/cmds-subvolume.c b/cmds-subvolume.c
index 82173c0..be1a54a 100644
--- a/cmds-subvolume.c
+++ b/cmds-subvolume.c
@@ -89,7 +89,7 @@ static int wait_for_subvolume_cleaning(int fd, int count, u64 *ids,
clean = 0;
}
}
- if (clean == 0)
+ if (clean)
break;
sleep(sleep_interval);
}
@@ -181,11 +181,9 @@ static int cmd_subvol_create(int argc, char **argv)
goto out;
}
- fddst = open_file_or_dir(dstdir, &dirstream);
- if (fddst < 0) {
- fprintf(stderr, "ERROR: can't access '%s'\n", dstdir);
+ fddst = btrfs_open_dir(dstdir, &dirstream, 1);
+ if (fddst < 0)
goto out;
- }
printf("Create subvolume '%s/%s'\n", dstdir, newname);
if (inherit) {
@@ -348,9 +346,8 @@ again:
vname = basename(dupvname);
free(cpath);
- fd = open_file_or_dir(dname, &dirstream);
+ fd = btrfs_open_dir(dname, &dirstream, 1);
if (fd < 0) {
- fprintf(stderr, "ERROR: can't access '%s'\n", dname);
ret = 1;
goto out;
}
@@ -564,7 +561,7 @@ static int cmd_subvol_list(int argc, char **argv)
}
subvol = argv[optind];
- fd = open_file_or_dir(subvol, &dirstream);
+ fd = btrfs_open_dir(subvol, &dirstream, 1);
if (fd < 0) {
ret = -1;
fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
@@ -723,17 +720,13 @@ static int cmd_subvol_snapshot(int argc, char **argv)
goto out;
}
- fddst = open_file_or_dir(dstdir, &dirstream1);
- if (fddst < 0) {
- fprintf(stderr, "ERROR: can't access '%s'\n", dstdir);
+ fddst = btrfs_open_dir(dstdir, &dirstream1, 1);
+ if (fddst < 0)
goto out;
- }
- fd = open_file_or_dir(subvol, &dirstream2);
- if (fd < 0) {
- fprintf(stderr, "ERROR: can't access '%s'\n", dstdir);
+ fd = btrfs_open_dir(subvol, &dirstream2, 1);
+ if (fd < 0)
goto out;
- }
if (readonly) {
args.flags |= BTRFS_SUBVOL_RDONLY;
@@ -791,11 +784,9 @@ static int cmd_subvol_get_default(int argc, char **argv)
usage(cmd_subvol_get_default_usage);
subvol = argv[1];
- fd = open_file_or_dir(subvol, &dirstream);
- if (fd < 0) {
- fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
+ fd = btrfs_open_dir(subvol, &dirstream, 1);
+ if (fd < 0)
return 1;
- }
ret = btrfs_list_get_default_subvolume(fd, &default_id);
if (ret) {
@@ -859,11 +850,9 @@ static int cmd_subvol_set_default(int argc, char **argv)
objectid = arg_strtou64(subvolid);
- fd = open_file_or_dir(path, &dirstream);
- if (fd < 0) {
- fprintf(stderr, "ERROR: can't access '%s'\n", path);
+ fd = btrfs_open_dir(path, &dirstream, 1);
+ if (fd < 0)
return 1;
- }
ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid);
e = errno;
@@ -906,11 +895,9 @@ static int cmd_subvol_find_new(int argc, char **argv)
return 1;
}
- fd = open_file_or_dir(subvol, &dirstream);
- if (fd < 0) {
- fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
+ fd = btrfs_open_dir(subvol, &dirstream, 1);
+ if (fd < 0)
return 1;
- }
ret = ioctl(fd, BTRFS_IOC_SYNC);
if (ret < 0) {
@@ -980,11 +967,9 @@ static int cmd_subvol_show(int argc, char **argv)
ret = 1;
svpath = get_subvol_name(mnt, fullpath);
- fd = open_file_or_dir(fullpath, &dirstream1);
- if (fd < 0) {
- fprintf(stderr, "ERROR: can't access '%s'\n", fullpath);
+ fd = btrfs_open_dir(fullpath, &dirstream1, 1);
+ if (fd < 0)
goto out;
- }
ret = btrfs_list_get_path_rootid(fd, &sv_id);
if (ret) {
@@ -993,11 +978,9 @@ static int cmd_subvol_show(int argc, char **argv)
goto out;
}
- mntfd = open_file_or_dir(mnt, &dirstream2);
- if (mntfd < 0) {
- fprintf(stderr, "ERROR: can't access '%s'\n", mnt);
+ mntfd = btrfs_open_dir(mnt, &dirstream2, 1);
+ if (mntfd < 0)
goto out;
- }
if (sv_id == BTRFS_FS_TREE_OBJECTID) {
printf("%s is btrfs root\n", fullpath);
@@ -1271,9 +1254,8 @@ static int cmd_subvol_sync(int argc, char **argv)
if (check_argc_min(argc - optind, 1))
usage(cmd_subvol_sync_usage);
- fd = open_file_or_dir(argv[optind], &dirstream);
+ fd = btrfs_open_dir(argv[optind], &dirstream, 1);
if (fd < 0) {
- fprintf(stderr, "ERROR: can't access '%s'\n", argv[optind]);
ret = 1;
goto out;
}
diff --git a/configure b/configure
index fd7e3f5..36d9b44 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.2.2.
+# Generated by GNU Autoconf 2.69 for btrfs-progs v4.3.
#
# 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.2.2'
-PACKAGE_STRING='btrfs-progs v4.2.2'
+PACKAGE_VERSION='v4.3'
+PACKAGE_STRING='btrfs-progs v4.3'
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.2.2 to adapt to many kinds of systems.
+\`configure' configures btrfs-progs v4.3 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.2.2:";;
+ short | recursive ) echo "Configuration of btrfs-progs v4.3:";;
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.2.2
+btrfs-progs configure v4.3
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.2.2, which was
+It was created by btrfs-progs $as_me v4.3, 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.2.2, which was
+This file was extended by btrfs-progs $as_me v4.3, 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.2.2
+btrfs-progs config.status v4.3
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
diff --git a/ctree.c b/ctree.c
index e6e5689..46153e3 100644
--- a/ctree.c
+++ b/ctree.c
@@ -1058,26 +1058,28 @@ int btrfs_find_item(struct btrfs_root *fs_root, struct btrfs_path *found_path,
path = found_path;
ret = btrfs_search_slot(NULL, fs_root, &key, path, 0, 0);
- if ((ret < 0) || (found_key == NULL)) {
- if (path != found_path)
- btrfs_free_path(path);
- return ret;
- }
+ if ((ret < 0) || (found_key == NULL))
+ goto out;
eb = path->nodes[0];
if (ret && path->slots[0] >= btrfs_header_nritems(eb)) {
ret = btrfs_next_leaf(fs_root, path);
if (ret)
- return ret;
+ goto out;
eb = path->nodes[0];
}
btrfs_item_key_to_cpu(eb, found_key, path->slots[0]);
if (found_key->type != key.type ||
- found_key->objectid != key.objectid)
- return 1;
+ found_key->objectid != key.objectid) {
+ ret = 1;
+ goto out;
+ }
- return 0;
+out:
+ if (path != found_path)
+ btrfs_free_path(path);
+ return ret;
}
/*
@@ -1617,13 +1619,14 @@ static int leaf_space_used(struct extent_buffer *l, int start, int nr)
*/
int btrfs_leaf_free_space(struct btrfs_root *root, struct extent_buffer *leaf)
{
+ u32 nodesize = (root ? BTRFS_LEAF_DATA_SIZE(root) : leaf->len);
int nritems = btrfs_header_nritems(leaf);
int ret;
- ret = BTRFS_LEAF_DATA_SIZE(root) - leaf_space_used(leaf, 0, nritems);
+ ret = nodesize - leaf_space_used(leaf, 0, nritems);
if (ret < 0) {
- printk("leaf free space ret %d, leaf data size %lu, used %d nritems %d\n",
- ret, (unsigned long) BTRFS_LEAF_DATA_SIZE(root),
- leaf_space_used(leaf, 0, nritems), nritems);
+ printk("leaf free space ret %d, leaf data size %u, used %d nritems %d\n",
+ ret, nodesize, leaf_space_used(leaf, 0, nritems),
+ nritems);
}
return ret;
}
diff --git a/disk-io.c b/disk-io.c
index c3da18c..139bc80 100644
--- a/disk-io.c
+++ b/disk-io.c
@@ -40,6 +40,8 @@
#define BTRFS_BAD_LEVEL (-3)
#define BTRFS_BAD_NRITEMS (-4)
+#define IS_ALIGNED(x, a) (((x) & ((typeof(x))(a) - 1)) == 0)
+
/* Calculate max possible nritems for a leaf/node */
static u32 max_nritems(u8 level, u32 nodesize)
{
@@ -696,10 +698,9 @@ struct btrfs_root *btrfs_read_fs_root_no_cache(struct btrfs_fs_info *fs_info,
u32 blocksize;
int ret = 0;
- root = malloc(sizeof(*root));
+ root = calloc(1, sizeof(*root));
if (!root)
return ERR_PTR(-ENOMEM);
- memset(root, 0, sizeof(*root));
if (location->offset == (u64)-1) {
ret = find_and_setup_root(tree_root, fs_info,
location->objectid, root);
@@ -827,12 +828,10 @@ struct btrfs_fs_info *btrfs_new_fs_info(int writable, u64 sb_bytenr)
{
struct btrfs_fs_info *fs_info;
- fs_info = malloc(sizeof(struct btrfs_fs_info));
+ fs_info = calloc(1, sizeof(struct btrfs_fs_info));
if (!fs_info)
return NULL;
- memset(fs_info, 0, sizeof(struct btrfs_fs_info));
-
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));
@@ -1323,12 +1322,148 @@ struct btrfs_root *open_ctree_fd(int fp, const char *path, u64 sb_bytenr,
return info->fs_root;
}
+/*
+ * Check if the super is valid:
+ * - nodesize/sectorsize - minimum, maximum, alignment
+ * - tree block starts - alignment
+ * - number of devices - something sane
+ * - sys array size - maximum
+ */
+static int check_super(struct btrfs_super_block *sb)
+{
+ char result[BTRFS_CSUM_SIZE];
+ u32 crc;
+ u16 csum_type;
+ int csum_size;
+
+ if (btrfs_super_magic(sb) != BTRFS_MAGIC) {
+ fprintf(stderr, "ERROR: superblock magic doesn't match\n");
+ return -EIO;
+ }
+
+ csum_type = btrfs_super_csum_type(sb);
+ if (csum_type >= ARRAY_SIZE(btrfs_csum_sizes)) {
+ fprintf(stderr, "ERROR: unsupported checksum algorithm %u\n",
+ csum_type);
+ return -EIO;
+ }
+ csum_size = btrfs_csum_sizes[csum_type];
+
+ crc = ~(u32)0;
+ crc = btrfs_csum_data(NULL, (char *)sb + BTRFS_CSUM_SIZE, crc,
+ BTRFS_SUPER_INFO_SIZE - BTRFS_CSUM_SIZE);
+ btrfs_csum_final(crc, result);
+
+ if (memcmp(result, sb->csum, csum_size)) {
+ fprintf(stderr, "ERROR: superblock checksum mismatch\n");
+ return -EIO;
+ }
+ if (btrfs_super_root_level(sb) >= BTRFS_MAX_LEVEL) {
+ fprintf(stderr, "ERROR: tree_root level too big: %d >= %d\n",
+ btrfs_super_root_level(sb), BTRFS_MAX_LEVEL);
+ return -EIO;
+ }
+ if (btrfs_super_chunk_root_level(sb) >= BTRFS_MAX_LEVEL) {
+ fprintf(stderr, "ERROR: chunk_root level too big: %d >= %d\n",
+ btrfs_super_chunk_root_level(sb), BTRFS_MAX_LEVEL);
+ return -EIO;
+ }
+ if (btrfs_super_log_root_level(sb) >= BTRFS_MAX_LEVEL) {
+ fprintf(stderr, "ERROR: log_root level too big: %d >= %d\n",
+ btrfs_super_log_root_level(sb), BTRFS_MAX_LEVEL);
+ return -EIO;
+ }
+
+ if (!IS_ALIGNED(btrfs_super_root(sb), 4096)) {
+ fprintf(stderr, "ERROR: tree_root block unaligned: %llu\n",
+ btrfs_super_root(sb));
+ return -EIO;
+ }
+ if (!IS_ALIGNED(btrfs_super_chunk_root(sb), 4096)) {
+ fprintf(stderr, "ERROR: chunk_root block unaligned: %llu\n",
+ btrfs_super_chunk_root(sb));
+ return -EIO;
+ }
+ if (!IS_ALIGNED(btrfs_super_log_root(sb), 4096)) {
+ fprintf(stderr, "ERROR: log_root block unaligned: %llu\n",
+ btrfs_super_log_root(sb));
+ return -EIO;
+ }
+ if (btrfs_super_nodesize(sb) < 4096) {
+ fprintf(stderr, "ERROR: nodesize too small: %u < 4096\n",
+ btrfs_super_nodesize(sb));
+ return -EIO;
+ }
+ if (!IS_ALIGNED(btrfs_super_nodesize(sb), 4096)) {
+ fprintf(stderr, "ERROR: nodesize unaligned: %u\n",
+ btrfs_super_nodesize(sb));
+ return -EIO;
+ }
+ if (btrfs_super_sectorsize(sb) < 4096) {
+ fprintf(stderr, "ERROR: sectorsize too small: %u < 4096\n",
+ btrfs_super_sectorsize(sb));
+ return -EIO;
+ }
+ if (!IS_ALIGNED(btrfs_super_sectorsize(sb), 4096)) {
+ fprintf(stderr, "ERROR: sectorsize unaligned: %u\n",
+ btrfs_super_sectorsize(sb));
+ return -EIO;
+ }
+
+ if (memcmp(sb->fsid, sb->dev_item.fsid, BTRFS_UUID_SIZE) != 0) {
+ char fsid[BTRFS_UUID_UNPARSED_SIZE];
+ char dev_fsid[BTRFS_UUID_UNPARSED_SIZE];
+
+ uuid_unparse(sb->fsid, fsid);
+ uuid_unparse(sb->dev_item.fsid, dev_fsid);
+ printk(KERN_ERR
+ "ERROR: dev_item UUID does not match fsid: %s != %s\n",
+ dev_fsid, fsid);
+ return -EIO;
+ }
+
+ /*
+ * Hint to catch really bogus numbers, bitflips or so
+ */
+ if (btrfs_super_num_devices(sb) > (1UL << 31)) {
+ fprintf(stderr, "WARNING: suspicious number of devices: %llu\n",
+ btrfs_super_num_devices(sb));
+ }
+
+ if (btrfs_super_num_devices(sb) == 0) {
+ fprintf(stderr, "ERROR: number of devices is 0\n");
+ return -EIO;
+ }
+
+ /*
+ * Obvious sys_chunk_array corruptions, it must hold at least one key
+ * and one chunk
+ */
+ if (btrfs_super_sys_array_size(sb) > BTRFS_SYSTEM_CHUNK_ARRAY_SIZE) {
+ fprintf(stderr, "BTRFS: system chunk array too big %u > %u\n",
+ btrfs_super_sys_array_size(sb),
+ BTRFS_SYSTEM_CHUNK_ARRAY_SIZE);
+ return -EIO;
+ }
+ if (btrfs_super_sys_array_size(sb) < sizeof(struct btrfs_disk_key)
+ + sizeof(struct btrfs_chunk)) {
+ fprintf(stderr, "BTRFS: system chunk array too small %u < %lu\n",
+ btrfs_super_sys_array_size(sb),
+ sizeof(struct btrfs_disk_key) +
+ sizeof(struct btrfs_chunk));
+ return -EIO;
+ }
+
+ return 0;
+}
+
int btrfs_read_dev_super(int fd, struct btrfs_super_block *sb, u64 sb_bytenr,
int super_recover)
{
u8 fsid[BTRFS_FSID_SIZE];
int fsid_is_initialized = 0;
- struct btrfs_super_block buf;
+ char tmp[BTRFS_SUPER_INFO_SIZE];
+ struct btrfs_super_block *buf = (struct btrfs_super_block *)tmp;
int i;
int ret;
int max_super = super_recover ? BTRFS_SUPER_MIRROR_MAX : 1;
@@ -1336,15 +1471,16 @@ int btrfs_read_dev_super(int fd, struct btrfs_super_block *sb, u64 sb_bytenr,
u64 bytenr;
if (sb_bytenr != BTRFS_SUPER_INFO_OFFSET) {
- ret = pread64(fd, &buf, sizeof(buf), sb_bytenr);
- if (ret < sizeof(buf))
+ ret = pread64(fd, buf, BTRFS_SUPER_INFO_SIZE, sb_bytenr);
+ if (ret < BTRFS_SUPER_INFO_SIZE)
return -1;
- if (btrfs_super_bytenr(&buf) != sb_bytenr ||
- btrfs_super_magic(&buf) != BTRFS_MAGIC)
+ if (btrfs_super_bytenr(buf) != sb_bytenr)
return -1;
- memcpy(sb, &buf, sizeof(*sb));
+ if (check_super(buf))
+ return -1;
+ memcpy(sb, buf, BTRFS_SUPER_INFO_SIZE);
return 0;
}
@@ -1357,22 +1493,22 @@ int btrfs_read_dev_super(int fd, struct btrfs_super_block *sb, u64 sb_bytenr,
for (i = 0; i < max_super; i++) {
bytenr = btrfs_sb_offset(i);
- ret = pread64(fd, &buf, sizeof(buf), bytenr);
- if (ret < sizeof(buf))
+ ret = pread64(fd, buf, BTRFS_SUPER_INFO_SIZE, bytenr);
+ if (ret < BTRFS_SUPER_INFO_SIZE)
break;
- if (btrfs_super_bytenr(&buf) != bytenr )
+ if (btrfs_super_bytenr(buf) != bytenr )
continue;
/* if magic is NULL, the device was removed */
- if (btrfs_super_magic(&buf) == 0 && i == 0)
- return -1;
- if (btrfs_super_magic(&buf) != BTRFS_MAGIC)
+ if (btrfs_super_magic(buf) == 0 && i == 0)
+ break;
+ if (check_super(buf))
continue;
if (!fsid_is_initialized) {
- memcpy(fsid, buf.fsid, sizeof(fsid));
+ memcpy(fsid, buf->fsid, sizeof(fsid));
fsid_is_initialized = 1;
- } else if (memcmp(fsid, buf.fsid, sizeof(fsid))) {
+ } else if (memcmp(fsid, buf->fsid, sizeof(fsid))) {
/*
* the superblocks (the original one and
* its backups) contain data of different
@@ -1381,9 +1517,9 @@ int btrfs_read_dev_super(int fd, struct btrfs_super_block *sb, u64 sb_bytenr,
continue;
}
- if (btrfs_super_generation(&buf) > transid) {
- memcpy(sb, &buf, sizeof(*sb));
- transid = btrfs_super_generation(&buf);
+ if (btrfs_super_generation(buf) > transid) {
+ memcpy(sb, buf, BTRFS_SUPER_INFO_SIZE);
+ transid = btrfs_super_generation(buf);
}
}
diff --git a/extent-tree.c b/extent-tree.c
index 0c8152a..e04d962 100644
--- a/extent-tree.c
+++ b/extent-tree.c
@@ -19,6 +19,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
+#include <math.h>
#include "kerncompat.h"
#include "radix-tree.h"
#include "ctree.h"
@@ -28,7 +29,6 @@
#include "crc32c.h"
#include "volumes.h"
#include "free-space-cache.h"
-#include "math.h"
#include "utils.h"
#define PENDING_EXTENT_INSERT 0
@@ -3255,7 +3255,7 @@ int btrfs_read_block_groups(struct btrfs_root *root)
cache = kzalloc(sizeof(*cache), GFP_NOFS);
if (!cache) {
ret = -ENOMEM;
- break;
+ goto error;
}
read_extent_buffer(leaf, &cache->item,
diff --git a/extent_io.c b/extent_io.c
index 07695ef..75496ce 100644
--- a/extent_io.c
+++ b/extent_io.c
@@ -538,12 +538,11 @@ static struct extent_buffer *__alloc_extent_buffer(struct extent_io_tree *tree,
{
struct extent_buffer *eb;
- eb = malloc(sizeof(struct extent_buffer) + blocksize);
+ eb = calloc(1, sizeof(struct extent_buffer) + blocksize);
if (!eb) {
BUG();
return NULL;
}
- memset(eb, 0, sizeof(struct extent_buffer) + blocksize);
eb->start = bytenr;
eb->len = blocksize;
diff --git a/inode-map.c b/inode-map.c
index 1321bfb..346952b 100644
--- a/inode-map.c
+++ b/inode-map.c
@@ -44,6 +44,7 @@ int btrfs_find_free_objectid(struct btrfs_trans_handle *trans,
BTRFS_FIRST_FREE_OBJECTID);
search_key.objectid = search_start;
search_key.offset = 0;
+ search_key.type = 0;
btrfs_init_path(path);
start_found = 0;
diff --git a/mkfs.c b/mkfs.c
index a5802f7..0ea39f3 100644
--- a/mkfs.c
+++ b/mkfs.c
@@ -42,6 +42,7 @@
#include "volumes.h"
#include "transaction.h"
#include "utils.h"
+#include "list_sort.h"
static u64 index_cnt = 2;
static int verbose = 1;
@@ -152,7 +153,7 @@ err:
}
static int make_root_dir(struct btrfs_trans_handle *trans, struct btrfs_root *root,
- int mixed, struct mkfs_allocation *allocation)
+ struct mkfs_allocation *allocation)
{
struct btrfs_key location;
int ret;
@@ -328,7 +329,6 @@ static void print_usage(int ret)
fprintf(stderr, "\t-U|--uuid UUID specify the filesystem UUID\n");
fprintf(stderr, "\t-q|--quiet no messages except errors\n");
fprintf(stderr, "\t-V|--version print the mkfs.btrfs version and exit\n");
- fprintf(stderr, "%s\n", PACKAGE_STRING);
exit(ret);
}
@@ -668,12 +668,11 @@ static int add_file_items(struct btrfs_trans_handle *trans,
* do our IO in extent buffers so it can work
* against any raid type
*/
- eb = malloc(sizeof(*eb) + sectorsize);
+ eb = calloc(1, sizeof(*eb) + sectorsize);
if (!eb) {
ret = -ENOMEM;
goto end;
}
- memset(eb, 0, sizeof(*eb) + sectorsize);
again:
@@ -927,7 +926,7 @@ fail_no_dir:
static int open_target(char *output_name)
{
int output_fd;
- output_fd = open(output_name, O_CREAT | O_RDWR | O_TRUNC,
+ output_fd = open(output_name, O_CREAT | O_RDWR,
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
return output_fd;
@@ -1148,6 +1147,13 @@ static int is_ssd(const char *file)
return !atoi((const char *)&rotational);
}
+static int _cmp_device_by_id(void *priv, struct list_head *a,
+ struct list_head *b)
+{
+ return list_entry(a, struct btrfs_device, dev_list)->devid -
+ list_entry(b, struct btrfs_device, dev_list)->devid;
+}
+
static void list_all_devices(struct btrfs_root *root)
{
struct btrfs_fs_devices *fs_devices;
@@ -1160,15 +1166,14 @@ static void list_all_devices(struct btrfs_root *root)
list_for_each_entry(device, &fs_devices->devices, dev_list)
number_of_devices++;
+ list_sort(NULL, &fs_devices->devices, _cmp_device_by_id);
+
printf("Number of devices: %d\n", number_of_devices);
/* printf("Total devices size: %10s\n", */
/* pretty_size(total_block_count)); */
printf("Devices:\n");
printf(" ID SIZE PATH\n");
- list_for_each_entry_reverse(device, &fs_devices->devices, dev_list) {
- char dev_uuid[BTRFS_UUID_UNPARSED_SIZE];
-
- uuid_unparse(device->uuid, dev_uuid);
+ list_for_each_entry(device, &fs_devices->devices, dev_list) {
printf(" %3llu %10s %s\n",
device->devid,
pretty_size(device->total_bytes),
@@ -1440,8 +1445,6 @@ int main(int ac, char **av)
break;
case 'b':
block_count = parse_size(optarg);
- if (block_count <= BTRFS_MKFS_SMALL_VOLUME_SIZE)
- mixed = 1;
zero_end = 0;
break;
case 'V':
@@ -1467,6 +1470,11 @@ int main(int ac, char **av)
}
}
+ if (verbose) {
+ printf("%s\n", PACKAGE_STRING);
+ printf("See %s for more information.\n\n", PACKAGE_URL);
+ }
+
sectorsize = max(sectorsize, (u32)sysconf(_SC_PAGESIZE));
saved_optind = optind;
dev_cnt = ac - optind;
@@ -1491,7 +1499,7 @@ int main(int ac, char **av)
exit(1);
}
}
-
+
while (dev_cnt-- > 0) {
file = av[optind++];
if (is_block_device(file) == 1)
@@ -1505,12 +1513,6 @@ int main(int ac, char **av)
file = av[optind++];
ssd = is_ssd(file);
- if (is_vol_small(file) || mixed) {
- if (verbose)
- printf("SMALL VOLUME: forcing mixed metadata/data groups\n");
- mixed = 1;
- }
-
/*
* Set default profiles according to number of added devices.
* For mixed groups defaults are single/single.
@@ -1544,6 +1546,19 @@ int main(int ac, char **av)
if (!nodesize_forced)
nodesize = best_nodesize;
}
+
+ /*
+ * FS features that can be set by other means than -O
+ * just set the bit here
+ */
+ if (mixed)
+ features |= BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS;
+
+ if ((data_profile | metadata_profile) &
+ (BTRFS_BLOCK_GROUP_RAID5 | BTRFS_BLOCK_GROUP_RAID6)) {
+ features |= BTRFS_FEATURE_INCOMPAT_RAID56;
+ }
+
if (btrfs_check_nodesize(nodesize, sectorsize,
features))
exit(1);
@@ -1583,12 +1598,6 @@ int main(int ac, char **av)
if (ret)
exit(1);
- /* if we are here that means all devs are good to btrfsify */
- if (verbose) {
- printf("%s\n", PACKAGE_STRING);
- printf("See %s for more information.\n\n", PACKAGE_URL);
- }
-
dev_cnt--;
if (!source_dir_set) {
@@ -1604,7 +1613,7 @@ int main(int ac, char **av)
exit(1);
}
ret = btrfs_prepare_device(fd, file, zero_end, &dev_block_count,
- block_count, &mixed, discard);
+ block_count, discard);
if (ret) {
close(fd);
exit(1);
@@ -1651,18 +1660,6 @@ int main(int ac, char **av)
"WARNING: metatdata has lower redundancy than data!\n\n");
}
- /*
- * FS features that can be set by other means than -O
- * just set the bit here
- */
- if (mixed)
- features |= BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS;
-
- if ((data_profile | metadata_profile) &
- (BTRFS_BLOCK_GROUP_RAID5 | BTRFS_BLOCK_GROUP_RAID6)) {
- features |= BTRFS_FEATURE_INCOMPAT_RAID56;
- }
-
mkfs_cfg.label = label;
mkfs_cfg.fs_uuid = fs_uuid;
memcpy(mkfs_cfg.blocks, blocks, sizeof(blocks));
@@ -1704,7 +1701,7 @@ int main(int ac, char **av)
exit(1);
}
- ret = make_root_dir(trans, root, mixed, &allocation);
+ ret = make_root_dir(trans, root, &allocation);
if (ret) {
fprintf(stderr, "failed to setup the root directory\n");
exit(1);
@@ -1725,8 +1722,6 @@ int main(int ac, char **av)
goto raid_groups;
while (dev_cnt-- > 0) {
- int old_mixed = mixed;
-
file = av[optind++];
/*
@@ -1749,12 +1744,11 @@ int main(int ac, char **av)
continue;
}
ret = btrfs_prepare_device(fd, file, zero_end, &dev_block_count,
- block_count, &mixed, discard);
+ block_count, discard);
if (ret) {
close(fd);
exit(1);
}
- mixed = old_mixed;
ret = btrfs_add_to_fsid(trans, root, fd, file, dev_block_count,
sectorsize, sectorsize, sectorsize);
diff --git a/print-tree.c b/print-tree.c
index dc1d276..4d4c3a2 100644
--- a/print-tree.c
+++ b/print-tree.c
@@ -231,9 +231,17 @@ void print_chunk(struct extent_buffer *eb, struct btrfs_chunk *chunk)
printf("\t\ttype %s num_stripes %d\n",
chunk_flags_str, num_stripes);
for (i = 0 ; i < num_stripes ; i++) {
+ unsigned char dev_uuid[BTRFS_UUID_SIZE];
+ char str_dev_uuid[BTRFS_UUID_UNPARSED_SIZE];
+
+ read_extent_buffer(eb, dev_uuid,
+ (unsigned long)btrfs_stripe_dev_uuid_nr(chunk, i),
+ BTRFS_UUID_SIZE);
+ uuid_unparse(dev_uuid, str_dev_uuid);
printf("\t\t\tstripe %d devid %llu offset %llu\n", i,
(unsigned long long)btrfs_stripe_devid_nr(eb, chunk, i),
(unsigned long long)btrfs_stripe_offset_nr(eb, chunk, i));
+ printf("\t\t\tdev uuid: %s\n", str_dev_uuid);
}
}
@@ -481,12 +489,13 @@ static void print_root(struct extent_buffer *leaf, int slot)
memset(&root_item, 0, sizeof(root_item));
read_extent_buffer(leaf, &root_item, (unsigned long)ri, len);
- printf("\t\troot data bytenr %llu level %d dirid %llu refs %u gen %llu\n",
+ printf("\t\troot data bytenr %llu level %d dirid %llu refs %u gen %llu lastsnap %llu\n",
(unsigned long long)btrfs_root_bytenr(&root_item),
btrfs_root_level(&root_item),
(unsigned long long)btrfs_root_dirid(&root_item),
btrfs_root_refs(&root_item),
- (unsigned long long)btrfs_root_generation(&root_item));
+ (unsigned long long)btrfs_root_generation(&root_item),
+ (unsigned long long)btrfs_root_last_snapshot(&root_item));
if (root_item.generation == root_item.generation_v2) {
uuid_unparse(root_item.uuid, uuid_str);
diff --git a/qgroup.c b/qgroup.c
index ec9a3ac..1fbfcb9 100644
--- a/qgroup.c
+++ b/qgroup.c
@@ -436,13 +436,12 @@ struct btrfs_qgroup_comparer_set *btrfs_qgroup_alloc_comparer_set(void)
size = sizeof(struct btrfs_qgroup_comparer_set) +
BTRFS_QGROUP_NCOMPS_INCREASE *
sizeof(struct btrfs_qgroup_comparer);
- set = malloc(size);
+ set = calloc(1, size);
if (!set) {
fprintf(stderr, "memory allocation failed\n");
exit(1);
}
- memset(set, 0, size);
set->total = BTRFS_QGROUP_NCOMPS_INCREASE;
return set;
@@ -644,12 +643,11 @@ static int add_qgroup(struct qgroup_lookup *qgroup_lookup, u64 qgroupid,
if (!ret)
return 0;
- bq = malloc(sizeof(*bq));
+ bq = calloc(1, sizeof(*bq));
if (!bq) {
printf("memory allocation failed\n");
exit(1);
}
- memset(bq, 0, sizeof(*bq));
if (qgroupid) {
bq->qgroupid = qgroupid;
INIT_LIST_HEAD(&bq->qgroups);
@@ -813,12 +811,11 @@ struct btrfs_qgroup_filter_set *btrfs_qgroup_alloc_filter_set(void)
size = sizeof(struct btrfs_qgroup_filter_set) +
BTRFS_QGROUP_NFILTERS_INCREASE *
sizeof(struct btrfs_qgroup_filter);
- set = malloc(size);
+ set = calloc(1, size);
if (!set) {
fprintf(stderr, "memory allocation failed\n");
exit(1);
}
- memset(set, 0, size);
set->total = BTRFS_QGROUP_NFILTERS_INCREASE;
return set;
@@ -1211,6 +1208,7 @@ int btrfs_show_qgroups(int fd,
__free_all_qgroups(&qgroup_lookup);
btrfs_qgroup_free_filter_set(filter_set);
+ btrfs_qgroup_free_comparer_set(comp_set);
return ret;
}
diff --git a/quick-test.c b/quick-test.c
index 5dfb2fe..ffde85d 100644
--- a/quick-test.c
+++ b/quick-test.c
@@ -46,8 +46,7 @@ int main(int ac, char **av) {
struct btrfs_root *root;
struct btrfs_trans_handle *trans;
- buf = malloc(512);
- memset(buf, 0, 512);
+ buf = calloc(1, 512);
radix_tree_init();
diff --git a/string-table.c b/string-table.c
index 701f214..7e01412 100644
--- a/string-table.c
+++ b/string-table.c
@@ -26,19 +26,19 @@
*/
struct string_table *table_create(int columns, int rows)
{
- struct string_table *p;
+ struct string_table *tab;
int size;
- size = sizeof( struct string_table ) +
- rows * columns* sizeof(char *);
- p = calloc(1, size);
+ size = sizeof(struct string_table) + rows * columns * sizeof(char*);
+ tab = calloc(1, size);
- if (!p) return NULL;
+ if (!tab)
+ return NULL;
- p->ncols = columns;
- p->nrows = rows;
+ tab->ncols = columns;
+ tab->nrows = rows;
- return p;
+ return tab;
}
/*
@@ -51,8 +51,8 @@ struct string_table *table_create(int columns, int rows)
char *table_vprintf(struct string_table *tab, int column, int row,
char *fmt, va_list ap)
{
- int idx = tab->ncols*row+column;
- char *msg = calloc(100, sizeof(char));
+ int idx = tab->ncols * row + column;
+ char *msg = calloc(100, 1);
if (!msg)
return NULL;
@@ -65,7 +65,6 @@ char *table_vprintf(struct string_table *tab, int column, int row,
return msg;
}
-
/*
* This function is like a printf, but store the results in a cell of
* the table.
@@ -89,68 +88,63 @@ char *table_printf(struct string_table *tab, int column, int row,
*/
void table_dump(struct string_table *tab)
{
- int sizes[tab->ncols];
- int i, j;
+ int sizes[tab->ncols];
+ int i, j;
- for (i = 0 ; i < tab->ncols ; i++) {
+ for (i = 0; i < tab->ncols; i++) {
sizes[i] = 0;
- for (j = 0 ; j < tab->nrows ; j++) {
- int idx = i + j*tab->ncols;
- int s;
+ for (j = 0; j < tab->nrows; j++) {
+ int idx = i + j * tab->ncols;
+ int len;
if (!tab->cells[idx])
continue;
- s = strlen(tab->cells[idx]) - 1;
- if (s < 1 || tab->cells[idx][0] == '=')
+ len = strlen(tab->cells[idx]) - 1;
+ if (len < 1 || tab->cells[idx][0] == '*')
continue;
- if (s > sizes[i])
- sizes[i] = s;
+ if (len > sizes[i])
+ sizes[i] = len;
}
}
+ for (j = 0; j < tab->nrows; j++) {
+ for (i = 0; i < tab->ncols; i++) {
+ int idx = i + j * tab->ncols;
+ char *cell = tab->cells[idx];
- for (j = 0 ; j < tab->nrows ; j++) {
- for (i = 0 ; i < tab->ncols ; i++) {
-
- int idx = i + j*tab->ncols;
- char *s = tab->cells[idx];
-
- if (!s|| !strlen(s)) {
+ if (!cell || !strlen(cell)) {
printf("%*s", sizes[i], "");
- } else if (s && s[0] == '=') {
+ } else if (cell && cell[0] == '*' && cell[1]) {
int k = sizes[i];
- while(k--)
- putchar('=');
+
+ while (k--)
+ putchar(cell[1]);
} else {
printf("%*s",
- s[0] == '<' ? -sizes[i] : sizes[i],
- s+1);
+ cell[0] == '<' ? -sizes[i] : sizes[i],
+ cell + 1);
}
if (i != (tab->ncols - 1))
putchar(' ');
}
putchar('\n');
}
-
}
/*
- * Deallocate a tabular and all its content
+ * Deallocate a table and all of its content
*/
-
void table_free(struct string_table *tab)
{
-
- int i, count;
+ int i, count;
count = tab->ncols * tab->nrows;
- for (i=0 ; i < count ; i++)
+ for (i = 0; i < count; i++)
if (tab->cells[i])
free(tab->cells[i]);
free(tab);
-
}
diff --git a/string-table.h b/string-table.h
index 657aae4..c1695d8 100644
--- a/string-table.h
+++ b/string-table.h
@@ -18,13 +18,11 @@
#define __STRING_TABLE_H__
struct string_table {
-
- int ncols, nrows;
- char *cells[];
-
+ int ncols;
+ int nrows;
+ char *cells[];
};
-
struct string_table *table_create(int columns, int rows);
char *table_printf(struct string_table *tab, int column, int row,
char *fmt, ...);
diff --git a/tests/README.md b/tests/README.md
new file mode 100644
index 0000000..be3bda8
--- /dev/null
+++ b/tests/README.md
@@ -0,0 +1,136 @@
+# Btrfs-progs tests
+
+Run the tests from the top directory:
+
+```shell
+$ make test
+$ make test-fsck
+$ make test-convert
+```
+
+or selectively from the `tests/` directory:
+
+```shell
+$ ./fsck-tests.sh
+$ ./misc-tests.sh
+```
+
+The verbose output of the tests is logged into a file named after the test
+category, eg. `fsck-tests-results.txt`.
+
+## Selective testing
+
+The test are prefixed by a number for ordering and uniquenes. To run a
+particular test use:
+
+```shell
+$ make TEST=MASK test
+```
+
+where `MASK` is a glob expression that will execute only tests
+that match the MASK. Here the test number comes handy:
+
+```shell
+$ make TEST=001\* test-fsck
+$ TEST=001\* ./fsck-tests.sh
+```
+
+will run the first test in fsck-tests subdirectory.
+
+
+## Test structure
+
+*tests/fsck-tests/:*
+
+ * tests targeted at bugs that are fixable by fsck
+
+*tests/convert-tests/:*
+
+ * coverage tests of ext2/3/4 and btrfs-convert options
+
+*tests/fuzz-tests/:*
+
+ * collection of fuzzed or crafted images
+ * tests that are supposed to run various utilities on the images and not
+ crash
+
+*tests/misc-tests/:*
+
+ * anything that does not fit to the above, the test driver script will only
+ execute `./test.sh` in the test directory
+
+*tests/common:*
+
+ * script with helpers
+
+*tests/test.img:*
+
+ * default testing image, the file is never deleted by the scripts but
+ truncated to 0 bytes, so it keeps it's permissions. It's eg. possible to
+ host it on NFS, make it `chmod a+w` for root.
+
+
+## Other tuning, environment variables
+
+### Instrumentation
+
+It's possible to wrap the tested commands to utilities that might do more
+checking or catch failures at runtime. This can be done by setting the
+`INSTRUMENT` environment variable:
+
+```shell
+INSTRUMENT=valgrind ./fuzz-tests.sh # in tests/
+make INSTRUMENT=valgrind test-fuzz # in the top directory
+```
+
+The variable is prepended to the command *unquoted*, all sorts of shell tricks
+are possible.
+
+Note: instrumentation is not applied to privileged commands (anything that uses
+the root helper).
+
+### Verbosity
+
+Setting the variable `TEST_LOG=tty` will print all commands executed by some of
+the wrappers (`run_check` etc), other commands are silent.
+
+### Permissions
+
+Some commands require root privileges (to mount/umount, access loop devices).
+It is assumed that `sudo` will work in some way (no password, password asked
+and cached). Note that instrumentation is not applied in this case, for safety
+reasons. You need to modify the test script instead.
+
+### Cleanup
+
+The tests are supposed to cleanup after themselves if they pass. In case of
+failure, the rest of the tests are skipped and intermediate files, mounts and
+loop devices are kept. This should help to investigate the test failure but at
+least the mounts and loop devices need to be cleaned before the next run.
+
+This is partially done by the script `clean-tests.sh`, you may want to check
+the loop devices as they are managed on a per-test basis.
+
+## New test
+
+1. Pick the category for the new test or fallback to `misc-tests` if not sure. For
+an easy start copy an existing `test.sh` script from some test that might be
+close to the purpose of your new test.
+
+* Use the highest unused number in the sequence, write a short descriptive title
+and join by dashes `-`.
+
+* Write a short description of the bug and how it's teste to the comment at the
+begining of `test.sh`.
+
+* Write the test commands, comment anything that's not obvious.
+
+* Test your test. Use the `TEST` variable to jump right to your test:
+```shell
+$ make TEST=012\* tests-misc # from top directory
+$ TEST=012\* ./misc-tests.sh # from tests/
+```
+
+* The commit changelog should reference a commit that either introduced or
+ fixed the bug (or both). Subject line of the shall mention the name of the
+ new directory for ease of search, eg. `btrfs-progs: tests: add 012-subvolume-sync-must-wait`
diff --git a/tests/common b/tests/common
index 63b0d9f..6178048 100644
--- a/tests/common
+++ b/tests/common
@@ -24,7 +24,12 @@ _not_run()
run_check()
{
echo "############### $@" >> $RESULTS 2>&1
- "$@" >> $RESULTS 2>&1 || _fail "failed: $@"
+ if [ "$TEST_LOG" = 'tty' ]; then echo "CMD: $@" > /dev/tty; fi
+ if [ "$1" = 'root_helper' ]; then
+ "$@" >> $RESULTS 2>&1 || _fail "failed: $@"
+ else
+ $INSTRUMENT "$@" >> $RESULTS 2>&1 || _fail "failed: $@"
+ fi
}
# same as run_check but the stderr+stdout output is duplicated on stdout and
@@ -32,14 +37,28 @@ run_check()
run_check_stdout()
{
echo "############### $@" >> $RESULTS 2>&1
- "$@" 2>&1 | tee -a $RESULTS || _fail "failed: $@"
+ if [ "$TEST_LOG" = 'tty' ]; then echo "CMD(stdout): $@" > /dev/tty; fi
+ if [ "$1" = 'root_helper' ]; then
+ "$@" 2>&1 | tee -a $RESULTS || _fail "failed: $@"
+ else
+ $INSTRUMENT "$@" 2>&1 | tee -a $RESULTS || _fail "failed: $@"
+ fi
}
# 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): $@"
+ if [ "$TEST_LOG" = 'tty' ]; then echo "CMD(mayfail): $@" > /dev/tty; fi
+ if [ "$1" = 'root_helper' ]; then
+ "$@" >> $RESULTS 2>&1
+ else
+ $INSTRUMENT "$@" >> $RESULTS 2>&1
+ fi
+ if [ $? != 0 ]; then
+ echo "failed (ignored): $@" >> $RESULTS
+ return 1
+ fi
}
check_prereq()
@@ -62,51 +81,66 @@ check_image()
run_check $TOP/btrfs check $image
}
-# Process all image dumps in a given directory,
+# Extract a usable image from packed formats
# - raw btrfs filesystem images, suffix .raw
# - dtto compressed by XZ, suffix .raw.xz
# - meta-dump images with suffix .img
# - dtto compressed by XZ, suffix .img.xz
+extract_image()
+{
+ local image
+ local cleanme
+
+ image="$1"
+ case "$image" in
+ *.img)
+ rm -f $image.restored
+ : ;;
+ *.img.xz)
+ xz --decompress --keep "$image" || \
+ _fail "failed to decompress image $image" >&2
+ image=${image%%.xz}
+ rm -f $image.restored
+ cleanme=$image
+ ;;
+ *.raw)
+ cp --sparse=auto $image $image.restored
+ ;;
+ *.raw.xz)
+ xz --decompress --keep "$image" || \
+ _fail "failed to decompress image $image" >&2
+ image=${image%%.xz}
+ mv "$image" "$image".restored
+ ;;
+ esac
+
+ if ! [ -f $image.restored ]; then
+ echo "restoring image $(basename $image)" >> $RESULTS
+ $TOP/btrfs-image -r $image $image.restored \
+ &>> $RESULTS \
+ || _fail "failed to restore image $image" >&2
+ fi
+
+ [ -f "$cleanme" ] && rm -f "$cleanme"
+
+ echo "$image.restored"
+}
+
+# Process all image dumps in a given directory
check_all_images()
{
- dir=$1
+ local dir
+ local extracted
+
+ dir="$1"
for image in $(find $dir \( -iname '*.img' -o \
-iname '*.img.xz' -o \
-iname '*.raw' -o \
-iname '*.raw.xz' \) | sort)
do
- cleanme=
- case "$image" in
- *.img)
- rm -f $image.restored
- : ;;
- *.img.xz)
- xz --decompress --keep "$image" || \
- _fail "failed to decompress image $image"
- image=${image%%.xz}
- rm -f $image.restored
- cleanme=$image
- ;;
- *.raw)
- cp --sparse=auto $image $image.restored
- ;;
- *.raw.xz)
- xz --decompress --keep "$image" || \
- _fail "failed to decompress image $image"
- image=${image%%.xz}
- mv "$image" "$image".restored
- ;;
- esac
-
- if ! [ -f $image.restored ]; then
- echo "restoring image $(basename $image)" >> $RESULTS
- $TOP/btrfs-image -r $image $image.restored || \
- _fail "failed to restore image $image"
- fi
-
- check_image $image.restored
-
- rm -f $image.restored $cleanme
+ extracted=$(extract_image "$image")
+ check_image "$extracted"
+ rm -f "$extracted"
done
}
@@ -139,7 +173,7 @@ root_helper()
setup_root_helper()
{
- if [ $UID -eq 0 ]; then
+ if [ $UID -eq 0 -o -n "$SUDO_HELPER" ]; then
return
fi
@@ -162,7 +196,7 @@ prepare_test_dev()
local size="$1"
[[ "$TEST_DEV" ]] && return
- [[ "$size" ]] || size='1G'
+ [[ "$size" ]] || size='2G'
echo "\$TEST_DEV not given, use $TOP/test/test.img as fallback" >> \
$RESULTS
diff --git a/tests/fsck-tests/018-leaf-crossing-stripes/default_case.raw.xz b/tests/fsck-tests/018-leaf-crossing-stripes/default_case.raw.xz
new file mode 100644
index 0000000..60eb2f9
--- /dev/null
+++ b/tests/fsck-tests/018-leaf-crossing-stripes/default_case.raw.xz
Binary files differ
diff --git a/tests/fsck-tests/018-leaf-crossing-stripes/test.sh b/tests/fsck-tests/018-leaf-crossing-stripes/test.sh
new file mode 100755
index 0000000..c453ab5
--- /dev/null
+++ b/tests/fsck-tests/018-leaf-crossing-stripes/test.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+source $TOP/tests/common
+
+check_prereq btrfs
+
+image=$(extract_image "./default_case.raw.xz")
+run_check_stdout $TOP/btrfs check "$image" 2>&1 |
+ grep -q "crossing stripe boundary" ||
+ _fail "no expected error message in the output"
+
+rm -f "$image"
diff --git a/tests/fuzz-tests.sh b/tests/fuzz-tests.sh
new file mode 100755
index 0000000..0e59832
--- /dev/null
+++ b/tests/fuzz-tests.sh
@@ -0,0 +1,41 @@
+#!/bin/bash
+#
+# misc tests on fuzzed or crafted images
+
+unset TOP
+unset LANG
+LANG=C
+SCRIPT_DIR=$(dirname $(readlink -f $0))
+TOP=$(readlink -f $SCRIPT_DIR/../)
+TEST_DEV=${TEST_DEV:-}
+RESULTS="$TOP/tests/fuzz-tests-results.txt"
+IMAGE="$TOP/tests/test.img"
+
+source $TOP/tests/common
+
+export TOP
+export RESULTS
+export LANG
+export IMAGE
+
+rm -f $RESULTS
+
+check_prereq btrfs
+
+# The tests are driven by their custom script called 'test.sh'
+
+for i in $(find $TOP/tests/fuzz-tests -maxdepth 1 -mindepth 1 -type d \
+ ${TEST:+-name "$TEST"} | sort)
+do
+ name=$(basename $i)
+ cd $i
+ if [ -x test.sh ]; then
+ echo "=== Entering $i" >> $RESULTS
+ echo " [TEST] $name"
+ ./test.sh
+ if [ $? -ne 0 ]; then
+ _fail "test failed for case $(basename $i)"
+ fi
+ fi
+ cd $TOP
+done
diff --git a/tests/fuzz-tests/001-simple-unmounted/test.sh b/tests/fuzz-tests/001-simple-unmounted/test.sh
new file mode 100755
index 0000000..bf01a3a
--- /dev/null
+++ b/tests/fuzz-tests/001-simple-unmounted/test.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+# iterate over all fuzzed images and run 'btrfs check'
+
+source $TOP/tests/common
+
+setup_root_helper
+check_prereq btrfs
+
+# redefine the one provided by common
+check_image() {
+ local image
+
+ image=$1
+ run_mayfail $TOP/btrfs check "$image"
+}
+
+check_all_images $TOP/tests/fuzz-tests/images
+
+exit 0
diff --git a/tests/misc-tests/008-leaf-crossing-stripes/test.sh b/tests/misc-tests/008-leaf-crossing-stripes/test.sh
new file mode 100755
index 0000000..0381806
--- /dev/null
+++ b/tests/misc-tests/008-leaf-crossing-stripes/test.sh
@@ -0,0 +1,25 @@
+#!/bin/bash
+# test if btrfs-convert creates a filesystem without leaf crossing stripes
+
+source $TOP/tests/common
+
+check_prereq btrfs-convert
+check_prereq btrfs
+
+# In my test, it happened in 514M~560M, 737M~769M, 929M~917M,
+# and HAVE_ERROR=((size + 1) / 2) % 2 if size >= 970
+#
+SIZE_FROM=514
+SIZE_END=560
+A_PRIME_NUM=17
+for ((size = SIZE_FROM; size <= SIZE_END; size += A_PRIME_NUM)); do
+ run_check truncate -s "$size"M "$IMAGE"
+ run_check mkfs.ext4 -F "$IMAGE"
+ run_check $TOP/btrfs-convert "$IMAGE"
+ run_check_stdout $TOP/btrfs check "$IMAGE" 2>&1 |
+ grep -q "crossing stripe boundary" &&
+ _fail "leaf crossing stripes after btrfs-convert"
+done
+
+# grep will expectedly fail
+exit 0
diff --git a/tests/misc-tests/009-subvolume-sync-must-wait/test.sh b/tests/misc-tests/009-subvolume-sync-must-wait/test.sh
new file mode 100755
index 0000000..66d38ea
--- /dev/null
+++ b/tests/misc-tests/009-subvolume-sync-must-wait/test.sh
@@ -0,0 +1,50 @@
+#!/bin/bash
+#
+# Verify that subovolume sync waits until the subvolume is cleaned
+
+source $TOP/tests/common
+
+check_prereq mkfs.btrfs
+setup_root_helper
+
+run_check truncate -s 2G $IMAGE
+run_check $TOP/mkfs.btrfs -f $IMAGE
+run_check $SUDO_HELPER mount $IMAGE $TEST_MNT
+run_check $SUDO_HELPER chmod a+rw $TEST_MNT
+
+cd $TEST_MNT
+
+for i in `seq 5`; do
+ run_check dd if=/dev/zero of=file$i bs=1M count=10
+done
+
+for sn in `seq 4`;do
+ run_check $SUDO_HELPER $TOP/btrfs subvolume snapshot . snap$sn
+ for i in `seq 10`; do
+ run_check dd if=/dev/zero of=snap$sn/file$i bs=1M count=10
+ done
+done
+
+run_check $SUDO_HELPER $TOP/btrfs subvolume list .
+run_check $SUDO_HELPER $TOP/btrfs subvolume list -d .
+
+idtodel=`run_check_stdout $SUDO_HELPER $TOP/btrfs inspect-internal rootid snap3`
+
+# delete, sync after some time
+run_check $SUDO_HELPER $TOP/btrfs subvolume delete -c snap3
+{ sleep 5; run_check $TOP/btrfs filesystem sync $TEST_MNT; } &
+
+run_check $SUDO_HELPER $TOP/btrfs subvolume sync . $idtodel
+
+if run_check_stdout $SUDO_HELPER $TOP/btrfs subvolume list -d . |
+ grep -q "ID $idtodel.*DELETED"; then
+ _fail "sync did not wait for the subvolume cleanup"
+fi
+
+run_check $TOP/btrfs filesystem sync $TEST_MNT
+run_check $SUDO_HELPER $TOP/btrfs subvolume list -d .
+
+wait
+cd ..
+
+run_check $SUDO_HELPER umount $TEST_MNT
diff --git a/tests/misc-tests/010-convert-delete-ext2-subvol/test.sh b/tests/misc-tests/010-convert-delete-ext2-subvol/test.sh
new file mode 100755
index 0000000..4893647
--- /dev/null
+++ b/tests/misc-tests/010-convert-delete-ext2-subvol/test.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+#
+# verify that convert rollback finds the ext2_subvolume intact and fails if it
+# was partially deleted
+
+source $TOP/tests/common
+
+check_prereq btrfs-convert
+check_prereq btrfs
+setup_root_helper
+prepare_test_dev
+
+run_check truncate -s 2G "$TEST_DEV"
+run_check mkfs.ext4 -F "$TEST_DEV"
+run_check $TOP/btrfs-convert "$TEST_DEV"
+run_check $TOP/btrfs-debug-tree "$TEST_DEV"
+run_check_mount_test_dev
+run_check $SUDO_HELPER $TOP/btrfs subvolume delete -c "$TEST_MNT/ext2_saved"
+run_check_umount_test_dev
+run_check $TOP/btrfs-debug-tree "$TEST_DEV"
+run_check_stdout $TOP/btrfs-convert --rollback "$TEST_DEV" |
+ grep -q 'is it deleted' || _fail "unexpected rollback"
+
+exit 0
diff --git a/tests/mkfs-tests.sh b/tests/mkfs-tests.sh
new file mode 100755
index 0000000..4780b54
--- /dev/null
+++ b/tests/mkfs-tests.sh
@@ -0,0 +1,44 @@
+#!/bin/bash
+#
+# mkfs.btrfs tests
+
+unset TOP
+unset LANG
+LANG=C
+SCRIPT_DIR=$(dirname $(readlink -f $0))
+TOP=$(readlink -f $SCRIPT_DIR/../)
+TEST_DEV=${TEST_DEV:-}
+RESULTS="$TOP/tests/mkfs-tests-results.txt"
+IMAGE="$TOP/tests/test.img"
+
+source $TOP/tests/common
+
+# Allow child test to use $TOP and $RESULTS
+export TOP
+export RESULTS
+# For custom script needs to verfiy recovery
+export LANG
+# For tests that only use a loop device
+export IMAGE
+
+rm -f $RESULTS
+
+check_prereq mkfs.btrfs
+check_prereq btrfs
+
+# The tests are driven by their custom script called 'test.sh'
+
+for i in $(find $TOP/tests/mkfs-tests -maxdepth 1 -mindepth 1 -type d \
+ ${TEST:+-name "$TEST"} | sort)
+do
+ echo " [TEST] $(basename $i)"
+ cd $i
+ echo "=== Entering $i" >> $RESULTS
+ if [ -x test.sh ]; then
+ ./test.sh
+ if [ $? -ne 0 ]; then
+ _fail "test failed for case $(basename $i)"
+ fi
+ fi
+ cd $TOP
+done
diff --git a/tests/mkfs-tests/001-basic-profiles/test.sh b/tests/mkfs-tests/001-basic-profiles/test.sh
new file mode 100755
index 0000000..0861f36
--- /dev/null
+++ b/tests/mkfs-tests/001-basic-profiles/test.sh
@@ -0,0 +1,89 @@
+#!/bin/bash
+# test various blockgroup profile combinations, use loop devices as block
+# devices
+
+source $TOP/tests/common
+
+check_prereq btrfs-show-super
+check_prereq mkfs.btrfs
+check_prereq btrfs
+
+ndevs=4
+declare -a devs
+dev1=
+
+setup_root_helper
+
+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_check $SUDO_HELPER losetup -d $dev
+ done
+ for i in `seq $ndevs`; do
+ truncate -s0 img$i
+ done
+ run_check $SUDO_HELPER losetup --list
+}
+
+test_get_info()
+{
+ run_check $TOP/btrfs-show-super $dev1
+ run_check $SUDO_HELPER $TOP/btrfs check $dev1
+ run_check $SUDO_HELPER mount $dev1 $TEST_MNT
+ run_check $TOP/btrfs filesystem df $TEST_MNT
+ run_check $SUDO_HELPER $TOP/btrfs filesystem usage $TEST_MNT
+ run_check $SUDO_HELPER $TOP/btrfs device usage $TEST_MNT
+ run_check $SUDO_HELPER umount "$TEST_MNT"
+}
+test_do_mkfs()
+{
+ run_check $SUDO_HELPER $TOP/mkfs.btrfs -f \
+ $@
+}
+
+test_mkfs_single()
+{
+ test_do_mkfs $@ $dev1
+ test_get_info
+}
+test_mkfs_multi()
+{
+ test_do_mkfs $@ ${devs[@]}
+ test_get_info
+}
+
+prepare_devices
+dev1=${devs[1]}
+
+test_mkfs_single
+test_mkfs_single -d single -m single
+test_mkfs_single -d single -m single --mixed
+test_mkfs_single -d single -m dup
+test_mkfs_single -d dup -m dup --mixed
+
+test_mkfs_multi
+test_mkfs_multi -d single -m single
+test_mkfs_multi -d single -m single --mixed
+test_mkfs_multi -d raid0 -m raid0
+test_mkfs_multi -d raid0 -m raid0 --mixed
+test_mkfs_multi -d raid1 -m raid1
+test_mkfs_multi -d raid1 -m raid1 --mixed
+test_mkfs_multi -d raid10 -m raid10
+test_mkfs_multi -d raid10 -m raid10 --mixed
+test_mkfs_multi -d raid5 -m raid5
+test_mkfs_multi -d raid5 -m raid5 --mixed
+test_mkfs_multi -d raid6 -m raid6
+test_mkfs_multi -d raid6 -m raid6 --mixed
+
+cleanup_devices
diff --git a/tests/mkfs-tests/002-no-force-mixed-on-small-volume/test.sh b/tests/mkfs-tests/002-no-force-mixed-on-small-volume/test.sh
new file mode 100755
index 0000000..007a0eb
--- /dev/null
+++ b/tests/mkfs-tests/002-no-force-mixed-on-small-volume/test.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+#
+# Verify that we do not force mixed block groups on small volumes anymore
+
+source $TOP/tests/common
+
+check_prereq btrfs-show-super
+check_prereq mkfs.btrfs
+setup_root_helper
+
+run_check truncate -s 512M $IMAGE
+mixed=$(run_check_stdout $TOP/mkfs.btrfs -n 64k -f $IMAGE | egrep 'Data|Metadata')
+echo "$mixed" | grep -q -v 'Data+Metadata:' || _fail "unexpected: created a mixed-bg filesystem"
diff --git a/tests/mkfs-tests/003-mixed-with-wrong-nodesize/test.sh b/tests/mkfs-tests/003-mixed-with-wrong-nodesize/test.sh
new file mode 100755
index 0000000..289d5ff
--- /dev/null
+++ b/tests/mkfs-tests/003-mixed-with-wrong-nodesize/test.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+#
+# Mixed mode needs equal sectorsize and nodesize
+
+source $TOP/tests/common
+
+check_prereq mkfs.btrfs
+
+run_check truncate -s 512M $IMAGE
+run_mayfail $TOP/mkfs.btrfs -f -M -s 4096 -n 16384 "$IMAGE" && _fail
+
+exit 0
diff --git a/tests/mkfs-tests/004-rootdir-keeps-size/test.sh b/tests/mkfs-tests/004-rootdir-keeps-size/test.sh
new file mode 100755
index 0000000..a78a3de
--- /dev/null
+++ b/tests/mkfs-tests/004-rootdir-keeps-size/test.sh
@@ -0,0 +1,30 @@
+#!/bin/bash
+# make sure that mkfs.btrfs --rootsize does not change size of the image
+
+source $TOP/tests/common
+
+check_prereq mkfs.btrfs
+prepare_test_dev
+
+test_mkfs_with_size() {
+ local size
+ local imgsize
+ local tmp
+
+ size="$1"
+ run_check truncate -s$size $TEST_DEV
+ imgsize=$(run_check_stdout stat --format=%s $TEST_DEV)
+ run_check $SUDO_HELPER $TOP/mkfs.btrfs -f \
+ --rootdir $TOP/Documentation \
+ $TEST_DEV
+ tmp=$(run_check_stdout stat --format=%s $TEST_DEV)
+ if ! [ "$imgsize" = "$tmp" ]; then
+ _fail "image size changed from $imgsize to $tmp"
+ fi
+}
+
+test_mkfs_with_size 128M
+test_mkfs_with_size 256M
+test_mkfs_with_size 512M
+test_mkfs_with_size 1G
+test_mkfs_with_size 2G
diff --git a/utils.c b/utils.c
index f1e3248..d546bea 100644
--- a/utils.c
+++ b/utils.c
@@ -724,7 +724,7 @@ static int zero_dev_clamped(int fd, off_t start, ssize_t len, u64 dev_size)
int btrfs_add_to_fsid(struct btrfs_trans_handle *trans,
struct btrfs_root *root, int fd, char *path,
- u64 block_count, u32 io_width, u32 io_align,
+ u64 device_total_bytes, u32 io_width, u32 io_align,
u32 sectorsize)
{
struct btrfs_super_block *disk_super;
@@ -732,10 +732,12 @@ int btrfs_add_to_fsid(struct btrfs_trans_handle *trans,
struct btrfs_device *device;
struct btrfs_dev_item *dev_item;
char *buf = NULL;
- u64 total_bytes;
+ u64 fs_total_bytes;
u64 num_devs;
int ret;
+ device_total_bytes = (device_total_bytes / sectorsize) * sectorsize;
+
device = kzalloc(sizeof(*device), GFP_NOFS);
if (!device)
goto err_nomem;
@@ -755,7 +757,7 @@ int btrfs_add_to_fsid(struct btrfs_trans_handle *trans,
device->sector_size = sectorsize;
device->fd = fd;
device->writeable = 1;
- device->total_bytes = block_count;
+ device->total_bytes = device_total_bytes;
device->bytes_used = 0;
device->total_ios = 0;
device->dev_root = root->fs_info->dev_root;
@@ -763,11 +765,12 @@ int btrfs_add_to_fsid(struct btrfs_trans_handle *trans,
if (!device->name)
goto err_nomem;
+ INIT_LIST_HEAD(&device->dev_list);
ret = btrfs_add_device(trans, root, device);
BUG_ON(ret);
- total_bytes = btrfs_super_total_bytes(super) + block_count;
- btrfs_set_super_total_bytes(super, total_bytes);
+ fs_total_bytes = btrfs_super_total_bytes(super) + device_total_bytes;
+ btrfs_set_super_total_bytes(super, fs_total_bytes);
num_devs = btrfs_super_num_devices(super) + 1;
btrfs_set_super_num_devices(super, num_devs);
@@ -847,7 +850,7 @@ out:
}
int btrfs_prepare_device(int fd, char *file, int zero_end, u64 *block_count_ret,
- u64 max_block_count, int *mixed, int discard)
+ u64 max_block_count, int discard)
{
u64 block_count;
struct stat st;
@@ -867,9 +870,6 @@ int btrfs_prepare_device(int fd, char *file, int zero_end, u64 *block_count_ret,
if (max_block_count)
block_count = min(block_count, max_block_count);
- if (block_count < BTRFS_MKFS_SMALL_VOLUME_SIZE && !(*mixed))
- *mixed = 1;
-
if (discard) {
/*
* We intentionally ignore errors from the discard ioctl. It
@@ -1081,27 +1081,28 @@ out:
*
* On error, return -1, errno should be set.
*/
-int open_path_or_dev_mnt(const char *path, DIR **dirstream)
+int open_path_or_dev_mnt(const char *path, DIR **dirstream, int verbose)
{
char mp[PATH_MAX];
- int fdmnt;
-
- fdmnt = is_block_device(path);
- if (fdmnt == 1) {
- int ret;
+ int ret;
+ if (is_block_device(path)) {
ret = get_btrfs_mount(path, mp, sizeof(mp));
if (ret < 0) {
/* not a mounted btrfs dev */
+ error_on(verbose, "'%s' is not a mounted btrfs device",
+ path);
errno = EINVAL;
return -1;
}
- fdmnt = open_file_or_dir(mp, dirstream);
- } else if (fdmnt == 0) {
- fdmnt = open_file_or_dir(path, dirstream);
+ ret = open_file_or_dir(mp, dirstream);
+ error_on(verbose && ret < 0, "can't access '%s': %s",
+ path, strerror(errno));
+ } else {
+ ret = btrfs_open_dir(path, dirstream, 1);
}
- return fdmnt;
+ return ret;
}
/*
@@ -3076,3 +3077,14 @@ unsigned int get_unit_mode_from_arg(int *argc, char *argv[], int df_mode)
return unit_mode;
}
+
+int string_is_numerical(const char *str)
+{
+ if (!(*str >= '0' && *str <= '9'))
+ return 0;
+ while (*str >= '0' && *str <= '9')
+ str++;
+ if (*str != '\0')
+ return 0;
+ return 1;
+}
diff --git a/utils.h b/utils.h
index 192f3d1..33b410c 100644
--- a/utils.h
+++ b/utils.h
@@ -22,6 +22,7 @@
#include <sys/stat.h>
#include "ctree.h"
#include <dirent.h>
+#include <stdarg.h>
#define BTRFS_MKFS_SYSTEM_GROUP_SIZE (4 * 1024 * 1024)
#define BTRFS_MKFS_SMALL_VOLUME_SIZE (1024 * 1024 * 1024)
@@ -120,7 +121,7 @@ int make_btrfs(int fd, struct btrfs_mkfs_config *cfg);
int btrfs_make_root_dir(struct btrfs_trans_handle *trans,
struct btrfs_root *root, u64 objectid);
int btrfs_prepare_device(int fd, char *file, int zero_end, u64 *block_count_ret,
- u64 max_block_count, int *mixed, int discard);
+ u64 max_block_count, int discard);
int btrfs_add_to_fsid(struct btrfs_trans_handle *trans,
struct btrfs_root *root, int fd, char *path,
u64 block_count, u32 io_width, u32 io_align,
@@ -157,7 +158,7 @@ char *__strncpy__null(char *dest, const char *src, size_t n);
int is_block_device(const char *file);
int is_mount_point(const char *file);
int check_arg_type(const char *input);
-int open_path_or_dev_mnt(const char *path, DIR **dirstream);
+int open_path_or_dev_mnt(const char *path, DIR **dirstream, int verbose);
int btrfs_open_dir(const char *path, DIR **dirstream, int verbose);
u64 btrfs_device_size(int fd, struct stat *st);
/* Helper to always get proper size of the destination string */
@@ -269,5 +270,60 @@ const char *get_argv0_buf(void);
"-t|--tbytes show sizes in TiB, or TB with --si"
unsigned int get_unit_mode_from_arg(int *argc, char *argv[], int df_mode);
+int string_is_numerical(const char *str);
+
+static inline void warning(const char *fmt, ...)
+{
+ va_list args;
+
+ fputs("WARNING: ", stderr);
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fputc('\n', stderr);
+}
+
+static inline void error(const char *fmt, ...)
+{
+ va_list args;
+
+ fputs("ERROR: ", stderr);
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fputc('\n', stderr);
+}
+
+static inline int warning_on(int condition, const char *fmt, ...)
+{
+ va_list args;
+
+ if (!condition)
+ return 0;
+
+ fputs("WARNING: ", stderr);
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fputc('\n', stderr);
+
+ return 1;
+}
+
+static inline int error_on(int condition, const char *fmt, ...)
+{
+ va_list args;
+
+ if (!condition)
+ return 0;
+
+ fputs("ERROR: ", stderr);
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fputc('\n', stderr);
+
+ return 1;
+}
#endif
diff --git a/version.sh b/version.sh
index 939ae9a..f8d022a 100755
--- a/version.sh
+++ b/version.sh
@@ -6,7 +6,7 @@
# Copyright 2008, Oracle
# Released under the GNU GPLv2
-v="v4.2.2"
+v="v4.3"
opt=$1
diff --git a/volumes.c b/volumes.c
index ca50f1c..83ddd16 100644
--- a/volumes.c
+++ b/volumes.c
@@ -1968,10 +1968,9 @@ static void split_eb_for_raid56(struct btrfs_fs_info *info,
if (raid_map[i] >= BTRFS_RAID5_P_STRIPE)
break;
- eb = malloc(sizeof(struct extent_buffer) + stripe_len);
+ eb = calloc(1, sizeof(struct extent_buffer) + stripe_len);
if (!eb)
BUG();
- memset(eb, 0, sizeof(struct extent_buffer) + stripe_len);
eb->start = raid_map[i];
eb->len = stripe_len;