summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDimitri John Ledkov <xnox@ubuntu.com>2016-01-19 19:05:48 +0000
committerDimitri John Ledkov <xnox@ubuntu.com>2016-01-19 19:05:48 +0000
commitaa0ade25843f5a33eea7ee7b6ea7b48a03f31755 (patch)
tree9e8c180813da21fc126a11e90b24aa7d71047f3e
parentbc395cc50a8c8e44eb6085d61e04b561d01b4408 (diff)
New upstream release.HEADdebian/4.4-1master
-rw-r--r--Documentation/Makefile.in6
-rw-r--r--Documentation/btrfs-balance.asciidoc58
-rw-r--r--Documentation/btrfs-filesystem.asciidoc221
-rw-r--r--Documentation/btrfs-man5.asciidoc406
-rw-r--r--Documentation/btrfs-mount.asciidoc251
-rw-r--r--Documentation/btrfs.asciidoc24
-rw-r--r--Documentation/mkfs.btrfs.asciidoc54
-rw-r--r--Makefile.in14
-rw-r--r--btrfs-calc-size.c20
-rw-r--r--btrfs-convert.c291
-rw-r--r--btrfs-corrupt-block.c17
-rw-r--r--btrfs-debug-tree.c27
-rw-r--r--btrfs-image.c38
-rw-r--r--btrfs-list.c32
-rw-r--r--btrfs-show-super.c79
-rw-r--r--btrfs.c9
-rw-r--r--btrfsck.h17
-rw-r--r--chunk-recover.c44
-rw-r--r--cmds-balance.c149
-rw-r--r--cmds-check.c143
-rw-r--r--cmds-device.c75
-rw-r--r--cmds-fi-usage.c95
-rw-r--r--cmds-filesystem.c111
-rw-r--r--cmds-inspect.c29
-rw-r--r--cmds-property.c96
-rw-r--r--cmds-qgroup.c35
-rw-r--r--cmds-quota.c8
-rw-r--r--cmds-receive.c208
-rw-r--r--cmds-replace.c45
-rw-r--r--cmds-rescue.c40
-rw-r--r--cmds-restore.c7
-rw-r--r--cmds-send.c119
-rw-r--r--cmds-subvolume.c150
-rw-r--r--commands.h2
-rwxr-xr-xconfigure98
-rw-r--r--configure.ac4
-rw-r--r--ctree.c1
-rw-r--r--ctree.h55
-rw-r--r--debian/changelog6
-rw-r--r--disk-io.c26
-rw-r--r--extent-cache.c57
-rw-r--r--extent-cache.h39
-rw-r--r--extent-tree.c118
-rw-r--r--extent_io.c6
-rw-r--r--extent_io.h2
-rw-r--r--find-root.c18
-rw-r--r--free-space-cache.c4
-rw-r--r--free-space-cache.h2
-rw-r--r--free-space-tree.c273
-rw-r--r--free-space-tree.h25
-rw-r--r--help.c10
-rw-r--r--ioctl.h32
-rw-r--r--kerncompat.h8
-rw-r--r--mkfs.c56
-rw-r--r--print-tree.c25
-rw-r--r--props.c2
-rw-r--r--qgroup-verify.c36
-rw-r--r--qgroup.c10
-rw-r--r--send-test.c2
-rw-r--r--send-utils.c8
-rw-r--r--string-table.c3
-rw-r--r--task-utils.c3
-rwxr-xr-xtests/convert-tests.sh6
-rwxr-xr-xtests/fsck-tests.sh2
-rwxr-xr-xtests/fsck-tests/006-bad-root-items/test.sh2
-rw-r--r--tests/fsck-tests/012-leaf-corruption/no_data_extent.tar.xzbin177600 -> 130260 bytes
-rwxr-xr-xtests/fsck-tests/012-leaf-corruption/test.sh13
-rwxr-xr-xtests/fsck-tests/013-extent-tree-rebuild/test.sh3
-rw-r--r--tests/fsck-tests/019-non-skinny-false-alert/default_case.img.xzbin0 -> 15236 bytes
-rwxr-xr-xtests/fsck-tests/019-non-skinny-false-alert/test.sh23
-rwxr-xr-xtests/fuzz-tests.sh2
-rw-r--r--tests/fuzz-tests/images/sys-array-num-stripes-0.raw.txt30
-rw-r--r--tests/fuzz-tests/images/sys-array-num-stripes-0.raw.xzbin0 -> 8364 bytes
-rwxr-xr-xtests/misc-tests.sh2
-rwxr-xr-xtests/misc-tests/001-btrfstune-features/test.sh3
-rwxr-xr-xtests/misc-tests/002-uuid-rewrite/test.sh2
-rwxr-xr-xtests/misc-tests/004-shrink-fs/test.sh2
-rwxr-xr-xtests/misc-tests/005-convert-progress-thread-crash/test.sh3
-rwxr-xr-xtests/misc-tests/009-subvolume-sync-must-wait/test.sh2
-rwxr-xr-xtests/misc-tests/010-convert-delete-ext2-subvol/test.sh2
-rwxr-xr-xtests/misc-tests/011-delete-missing-device/test.sh83
-rwxr-xr-xtests/mkfs-tests.sh2
-rwxr-xr-xtests/mkfs-tests/001-basic-profiles/test.sh2
-rwxr-xr-xtests/mkfs-tests/002-no-force-mixed-on-small-volume/test.sh2
-rwxr-xr-xtests/mkfs-tests/004-rootdir-keeps-size/test.sh1
-rwxr-xr-xtests/mkfs-tests/005-long-device-name-for-ssd/test.sh40
-rw-r--r--tests/mkfs-tests/006-partitioned-loopdev/partition-1g-1gbin0 -> 512 bytes
-rwxr-xr-xtests/mkfs-tests/006-partitioned-loopdev/test.sh27
-rwxr-xr-xtests/mkfs-tests/007-mix-nodesize-sectorsize/test.sh36
-rwxr-xr-xtests/mkfs-tests/008-secorsize-nodesize-combination/test.sh50
-rw-r--r--utils.c127
-rw-r--r--utils.h15
-rwxr-xr-xversion.sh2
-rw-r--r--volumes.c127
-rw-r--r--volumes.h19
95 files changed, 3107 insertions, 1372 deletions
diff --git a/Documentation/Makefile.in b/Documentation/Makefile.in
index 4711ad1..f046abd 100644
--- a/Documentation/Makefile.in
+++ b/Documentation/Makefile.in
@@ -31,8 +31,8 @@ MAN8_TXT += btrfs-replace.asciidoc
MAN8_TXT += btrfs-restore.asciidoc
MAN8_TXT += btrfs-property.asciidoc
-# Mount manpage
-MAN5_TXT += btrfs-mount.asciidoc
+# Category 5 manual page
+MAN5_TXT += btrfs-man5.asciidoc
MAN_TXT = $(MAN8_TXT) $(MAN5_TXT)
MAN_XML = $(patsubst %.asciidoc,%.xml,$(MAN_TXT))
@@ -92,7 +92,7 @@ install-man: man
$(INSTALL) -m 644 $(GZ_MAN5) $(DESTDIR)$(man5dir)
# the source file name of btrfs.5 clashes with section 8 page, but we
# want to keep the code generic
- $(MV) $(DESTDIR)$(man5dir)/btrfs-mount.5.gz $(DESTDIR)$(man5dir)/btrfs.5.gz
+ $(MV) $(DESTDIR)$(man5dir)/btrfs-man5.5.gz $(DESTDIR)$(man5dir)/btrfs.5.gz
$(INSTALL) -m 644 $(GZ_MAN8) $(DESTDIR)$(man8dir)
$(LN_S) -f btrfs-check.8.gz $(DESTDIR)$(man8dir)/btrfsck.8.gz
$(LN_S) -f btrfs-rescue.8.gz $(DESTDIR)$(man8dir)/btrfs-zero-log.8.gz
diff --git a/Documentation/btrfs-balance.asciidoc b/Documentation/btrfs-balance.asciidoc
index 7374a53..c840741 100644
--- a/Documentation/btrfs-balance.asciidoc
+++ b/Documentation/btrfs-balance.asciidoc
@@ -43,9 +43,9 @@ 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
-used anymore.
+NOTE: The balance subcommand also exists under the *btrfs filesystem*
+namespace. This still works for backward compatibility but is deprecated and
+should not be used anymore.
NOTE: A short syntax *btrfs balance <path>* works due to backward compatibility
but is deprecated and should not be used anymore. Use *btrfs balance start*
@@ -102,22 +102,28 @@ Balances only block groups with the given profiles. Parameters
are a list of profile names separated by "'|'" (pipe).
*usage=<percent>*::
+*usage=<range>*::
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
-case balance is returnin ENOSPC and your filesystem is not too full.
+should not require any new work space allocated. You may want to use 'usage=0'
+in case balance is returnin ENOSPC and your filesystem is not too full.
++
+The argument may be a single value or a range. The single value 'N' means 'at
+most N percent used', equivalent to '..N' range syntax. Kernels prior to 4.4
+accept only the single value format.
+The minimum range boundary is inclusive, maximum is exclusive.
*devid=<id>*::
Balances only block groups which have at least one chunk on the given
-device. To list devices with ids use 'btrfs fi show'.
+device. To list devices with ids use *btrfs fi show*.
*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
+Balance 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'.
*vrange=<range>*::
-Balances only block groups which overlap with the given byte range in the
+Balance 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'.
@@ -125,26 +131,42 @@ specified as 'start..end'.
*convert=<profile>*::
Convert each selected block group to the given profile name identified by
parameters.
++
+NOTE: starting with kernel 4.5, the 'data' chunks can be converted to/from the
+'DUP' profile on a single device.
*limit=<number>*::
+*limit=<range>*::
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.
+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.
++
+The argument may be a single value or a range. The single value 'N' means 'at
+most N chunks', equivalent to '..N' range syntax. Kernels prior to 4.4 accept
+only the single value format. The range minimum and maximum are inclusive.
+
+*stripes=<range>*::
+Balance only block groups which have the given number of stripes. The parameter
+is a range specified as 'start..end'. Makes sense fo block group profiles that
+utilize striping, ie. RAID0/10/5/6. The range minimum and maximum are
+inclusive.
*soft*::
Takes no parameters. Only has meaning when converting between profiles.
When doing convert from one profile to another and soft mode is on,
-chunks that alread yave the target profile are left untouched
- This is useful if e.g. half of the filesystem was converted earlier.
+chunks that already have the target profile are left untouched.
+This is useful e.g. when half of the filesystem was converted earlier but got
+cancelled.
+
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',
+Profile names, used in 'profiles' and 'convert' are one of: 'raid0', 'raid1',
'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.
+can be converted in the same way, but it's conversion between mixed and non-mixed
+is not implemented. For the constraints of the profiles please refer to `mkfs.btrfs`(8),
+section 'PROFILES'.
ENOSPC
------
@@ -155,13 +177,15 @@ 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'
+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'
diff --git a/Documentation/btrfs-filesystem.asciidoc b/Documentation/btrfs-filesystem.asciidoc
index 31cd51b..2612617 100644
--- a/Documentation/btrfs-filesystem.asciidoc
+++ b/Documentation/btrfs-filesystem.asciidoc
@@ -3,7 +3,7 @@ btrfs-filesystem(8)
NAME
----
-btrfs-filesystem - control btrfs filesystem
+btrfs-filesystem - command group of btrfs that usually work on the whole filesystem
SYNOPSIS
--------
@@ -11,14 +11,49 @@ SYNOPSIS
DESCRIPTION
-----------
-*btrfs filesystem* is used to do the filesystem level control jobs, including
-all the regular filesystem operations like setting/getting label,
-resizing, defragment.
+*btrfs filesystem* is used to do the whole filesystem level tasks, including
+all the regular filesystem operations like resizing, space stats, label
+setting/getting, and defragmentation.
SUBCOMMAND
----------
*df* [options] <path>::
-Show space usage information for a mount point.
+Show a terse summary information about allocation of block group types of a given
+mount point. The original purpose of this command was a debugging helper. The
+output needs to be further interpreted and is not suitable for quick overview.
++
+--
+An example with description:
+
+* device size: '1.9TiB', one device, no RAID
+* filesystem size: '1.9TiB'
+* created with: 'mkfs.btrfs -d single -m single'
+--
++
+------------------------------
+$ btrfs filesystem df /path
+Data, single: total=1.15TiB, used=1.13TiB
+System, single: total=32.00MiB, used=144.00KiB
+Metadata, single: total=12.00GiB, used=6.45GiB
+GlobalReserve, single: total=512.00MiB, used=0.00B
+------------------------------
++
+--
+* 'Data', 'System' and 'Metadata' are separeate block group types.
+'GlobalReserve' is an artificial and internal emergency space, see below.
+* 'single' -- the allocation profile, defined at mkfs time
+* 'total' -- sum of space reserved for
+all allocation profiles of the given type, ie. all Data/single. Note that it's
+not total size of filesystem.
+* 'used' -- sum of used space of the above, ie. file extents, metadata blocks
+--
++
+'GlobalReserve' is an artificial and internal emergency space. It is used eg.
+when the filesystem is full. Its 'total' size is dynamic based on the
+filesystem size, usually not larger than 512MiB, 'used' may fluctuate.
++
+The global block reserve is accounted within Metadata. In case the filesystem
+metadata are exhausted, 'GlobalReserve/total + Metadata/used = Metadata/total'.
+
`Options`
+
@@ -44,7 +79,7 @@ show sizes in TiB, or TB with --si
If conflicting options are passed, the last one takes precedence.
*defragment* [options] <file>|<dir> [<file>|<dir>...]::
-Defragment file data and/or directory metadata *online*.
+Defragment file data on a mounted filesytem.
+
If '-r' is passed, files in dir will be defragmented recursively.
The start position and the number of bytes to defragment can be specified by
@@ -55,14 +90,21 @@ if the free space is too fragmented.
Use 0 to take the kernel default, which is 256kB but may change in the future.
You can also turn on compression in defragment operations.
+
+WARNING: Defragmenting with Linux kernel versions < 3.9 or ≥ 3.14-rc2 as well as
+with Linux stable kernel versions ≥ 3.10.31, ≥ 3.12.12 or ≥ 3.13.4 will break up
+the ref-links of COW data (for example files copied with `cp --reflink`,
+snapshots or de-duplicated data).
+This may cause considerable increase of space usage depending on the broken up
+ref-links.
++
`Options`
+
-v::::
-be verbose
+be verbose, print file names as they're submitted for defragmentation
-c[<algo>]::::
compress file contents while defragmenting. Optional argument selects the compression
algorithm, 'zlib' (default) or 'lzo'. Currently it's not possible to select no
-compression.
+compression. See also section 'EXAMPLES'.
-r::::
defragment files recursively in given directories
-f::::
@@ -70,49 +112,51 @@ flush data for each file before going to the next file. This will limit the amou
of dirty data to current file, otherwise the amount cumulates from several files
and may increase system load.
-s <start>[kKmMgGtTpPeE]::::
-defragment only from byte <start> onward
+defragmentation will start from the given offset, default is beginning of a file
-l <len>[kKmMgGtTpPeE]::::
-defragment only up to <len> bytes
+defragment only up to 'len' bytes, default is the file size
-t <size>[kKmMgGtTpPeE]::::
-target extent size, do not touch extents bigger than <size>
+target extent size, do not touch extents bigger than 'size'
+
-For <start>, <len>, <size> it is possible to append
+For 'start', 'len', 'size' it is possible to append
units designator: \'K', \'M', \'G', \'T', \'P', or \'E', which represent
-KiB, MiB, GiB, TiB, PiB, or EiB, respectively. Case does not matter.
+KiB, MiB, GiB, TiB, PiB, or EiB, respectively (case does not matter).
+
-WARNING: defragmenting with kernels up to 2.6.37 will unlink COW-ed copies of data,
-don't use it if you use snapshots, have de-duplicated your data or made
-copies with `cp --reflink`.
+NOTE: Directory arguments without '-r' do not defragment files recursively but will
+defragment certain internal trees (extent tree and the subvolume tree). This has been
+confusing and could be removed in the future.
*label* [<dev>|<mountpoint>] [<newlabel>]::
-Show or update the label of a filesystem.
+Show or update the label of a filesystem. This works on a mounted filesystem or
+a filesystem image.
+
-[<device>|<mountpoint>] is used to identify the filesystem.
-If a newlabel optional argument is passed, the label is changed.
+The 'newlabel' argument is optional. Current label is printed if the the argument
+is omitted.
+
-NOTE: the maximum allowable length shall be less than 256 chars
+NOTE: the maximum allowable length shall be less than 256 chars and must not contain
+a newline. The trailing newline is stripped automatically.
// Some wording are extracted by the resize2fs man page
*resize* [<devid>:][+/-]<size>[kKmMgGtTpPeE]|[<devid>:]max <path>::
-Resize a mounted filesystem identified by directory <path>. A particular device
-can be resized by specifying a <devid>.
+Resize a mounted filesystem identified by 'path'. A particular device
+can be resized by specifying a 'devid'.
+
-If <path> is a file containing a btrfs image then resize does not work as
-expected and does not resize the image. This would resize the underlying
+WARNING: If 'path' is a file containing a BTRFS image then resize does not work
+as expected and does not resize the image. This would resize the underlying
filesystem instead.
+
-The devid can be found with *btrfs filesystem show* and
+The 'devid' can be found in the output of *btrfs filesystem show* and
defaults to 1 if not specified.
-The <size> parameter specifies the new size of the filesystem.
-If the prefix + or - is present the size is increased or decreased
-by the quantity <size>.
-If no units are specified, the unit of the <size> parameter defaults to
-bytes. Optionally, the size parameter may be suffixed by one of the following
+The 'size' parameter specifies the new size of the filesystem.
+If the prefix '+' or '-' is present the size is increased or decreased
+by the quantity 'size'.
+If no units are specified, bytes are assumed for 'size'.
+Optionally, the size parameter may be suffixed by one of the following
units designators: \'K', \'M', \'G', \'T', \'P', or \'E', which represent
-KiB, MiB, GiB, TiB, PiB, or EiB, respectively. Case does not matter.
+KiB, MiB, GiB, TiB, PiB, or EiB, respectively (case does not matter).
+
-If \'max' is passed, the filesystem will occupy all available space on the
-device devid.
+If 'max' is passed, the filesystem will occupy all available space on the
+device respecting 'devid' (remember, devid 1 by default).
+
The resize command does not manipulate the size of underlying
partition. If you wish to enlarge/reduce a filesystem, you must make sure you
@@ -120,21 +164,29 @@ can expand the partition before enlarging the filesystem and shrink the
partition after reducing the size of the filesystem. This can done using
`fdisk`(8) or `parted`(8) to delete the existing partition and recreate
it with the new desired size. When recreating the partition make sure to use
-the same starting disk cylinder as before.
+the same starting partition offset as before.
++
+Growing is usually instant as it only updates the size. However, shrinking could
+take a long time if there are data in the device area that's beyond the new
+end. Relocation of the data takes time.
++
+See also section 'EXAMPLES'.
*show* [options] [<path>|<uuid>|<device>|<label>]::
-Show the btrfs filesystem with some additional info.
+Show the btrfs filesystem with some additional info about devices and space
+allocation.
+
-If no option nor <path>|<uuid>|<device>|<label> is passed, btrfs shows
-information of all the btrfs filesystem both mounted and unmounted.
+If no option none of 'path'/'uuid'/'device'/'label' is passed, information
+about all the BTRFS filesystems is shown, both mounted and unmounted.
+
`Options`
+
-m|--mounted::::
-probe btrfs kernel to list mounted btrfs filesystems(s)
+probe kernel for mounted BTRFS filesystems
-d|--all-devices::::
scan all devices under /dev, otherwise the devices list is extracted from the
-/proc/partitions file.
+/proc/partitions file. This is a fallback option if there's no device node
+manager (like udev) available in the system.
--raw::::
raw numbers in bytes, without the 'B' suffix
--human-readable::::
@@ -153,10 +205,48 @@ show sizes in GiB, or GB with --si
show sizes in TiB, or TB with --si
*sync* <path>::
-Force a sync for the filesystem identified by <path>.
+Force a sync of the filesystem at 'path'. This is done via a special ioctl and
+will also trigger cleaning of deleted subvolumes. Besides that it's equivalent
+to the `sync`(1) command.
*usage* [options] <path> [<path>...]::
-Show detailed information about internal filesystem usage.
+Show detailed information about internal filesystem usage. This is supposed to
+replace the *btrfs filesystem df* command in the long run.
++
+The level of detail can differ if the command is run under a regular or the
+root user (due to use of restricted ioctl). For both there's a summary section
+with information about space usage:
++
+-------------------------
+$ btrfs fi usage /path
+WARNING: cannot read detailed chunk info, RAID5/6 numbers will be incorrect, run as root
+Overall:
+ Device size: 1.82TiB
+ Device allocated: 1.17TiB
+ Device unallocated: 669.99GiB
+ Device missing: 0.00B
+ Used: 1.14TiB
+ Free (estimated): 692.57GiB (min: 692.57GiB)
+ Data ratio: 1.00
+ Metadata ratio: 1.00
+ Global reserve: 512.00MiB (used: 0.00B)
+-------------------------
++
+The root user will also see stats broken down by block group types:
++
+-------------------------
+Data,single: Size:1.15TiB, Used:1.13TiB
+ /dev/sdb 1.15TiB
+
+Metadata,single: Size:12.00GiB, Used:6.45GiB
+ /dev/sdb 12.00GiB
+
+System,single: Size:32.00MiB, Used:144.00KiB
+ /dev/sdb 32.00MiB
+
+Unallocated:
+ /dev/sdb 669.99GiB
+-------------------------
+
`Options`
+
@@ -183,6 +273,55 @@ show data in tabular format
+
If conflicting options are passed, the last one takes precedence.
+EXAMPLES
+--------
+
+*$ btrfs filesystem defrag -v -r dir/*
+
+Recursively defragment files under 'dir/', print files as they are processed.
+The file names will be printed in batches, similarly the amount of data triggered
+by defragmentation will be proportional to last N printed files. The system dirty
+memory throttling will slow down the defragmentation but there can still be a lot
+of IO load and the system may stall for a moment.
+
+*$ btrfs filesystem defrag -v -r -f dir/*
+
+Recusively defragment files under 'dir/', be verbose and wait until all blocks
+are flushed before processing next file. You can note slower progress of the
+output and lower IO load (proportional to currently defragmented file).
+
+*$ btrfs filesystem defrag -v -r -f -clzo dir/*
+
+Recusively defragment files under 'dir/', be verbose, wait until all blocks are
+flushed and force file compression.
+
+*$ btrfs filesystem defrag -v -r -t 64M dir/*
+
+Recusively defragment files under 'dir/', be verbose and try to merge extents
+to be about 64MiB. As stated above, the success rate depends on actual free
+space fragmentation and the final result is not guaranteed to meet the target
+even if run repeatedly.
+
+*$ btrfs filesystem resize -1G /path*
+
+*$ btrfs filesystem resize 1:-1G /path*
+
+Shrink size of the filesystem's device id 1 by 1GiB. The first syntax expects a
+device with id 1 to exist, otherwise fails. The second is equivalent and more
+explicit. For a single-device filesystem it's typically not necessary to
+specify the devid though.
+
+*$ btrfs filesystem resize max /path*
+
+*$ btrfs filesystem resize 1:max /path*
+
+Let's assume that devid 1 exists, the filesystem does not occupy the whole block
+device, eg. it has been enlarged and we wan the grow the filesystem. Simply using
+'max' as size we will achieve that.
+
+NOTE: There are two ways to minimize the filesystem on a given device. The
+*btrfs inspect-internal min-dev-size* command, or iteratively shrink in steps.
+
EXIT STATUS
-----------
*btrfs filesystem* returns a zero exit status if it succeeds. Non zero is
diff --git a/Documentation/btrfs-man5.asciidoc b/Documentation/btrfs-man5.asciidoc
new file mode 100644
index 0000000..d432391
--- /dev/null
+++ b/Documentation/btrfs-man5.asciidoc
@@ -0,0 +1,406 @@
+btrfs-man5(5)
+==============
+
+NAME
+----
+btrfs-man5 - topics about the BTRFS filesystem (mount options, supported file attributes and other)
+
+DESCRIPTION
+-----------
+This document describes topics related to BTRFS that are not specific to the
+tools. Currently covers:
+
+1. mount options
+
+2. file attributes
+
+MOUNT OPTIONS
+-------------
+
+This section describes mount options specific to BTRFS. For the generic mount
+options please refer to `mount`(8) manpage. The options are sorted alphabetically
+(discarding the 'no' prefix).
+
+*acl*::
+*noacl*::
+(default: on)
++
+Enable/disable support for Posix Access Control Lists (ACLs). See the
+`acl`(5) manual page for more information about ACLs.
+
+*alloc_start='bytes'*::
+(default: 1M, minimum: 1M)
++
+Debugging option to force all block allocations above a certain
+byte threshold on each block device. The value is specified in
+bytes, optionally with a K, M, or G suffix (case insensitive).
++
+This option was used for testing and has not practial use, it's slated to be
+removed in the future.
+
+*autodefrag*::
+*noautodefrag*::
+(since: 3.0, default: off)
++
+Enable automatic file defragmentation.
+When enabled, small random writes into files (in a range of tens of kilobytes,
+currently it's 64K) are detected and queued up for the defragmentation process.
+Not well suited for large database workloads.
++
+The read latency may increase due to reading the adjacent blocks that make up the
+range for defragmentation, successive write will merge the blocks in the new
+location.
++
+WARNING: Defragmenting with Linux kernel versions < 3.9 or ≥ 3.14-rc2 as
+well as with Linux stable kernel versions ≥ 3.10.31, ≥ 3.12.12 or
+≥ 3.13.4 will break up the ref-links of CoW data (for example files
+copied with `cp --reflink`, snapshots or de-duplicated data).
+This may cause considerable increase of space usage depending on the
+broken up ref-links.
+
+*barrier*::
+*nobarrier*::
+(default: on)
++
+Ensure that all IO write operations make it through the device cache and are stored
+permanently when the filesystem is at it's consistency checkpoint. This
+typically means that a flush command is sent to the device that will
+synchronize all pending data and ordinary metadata blocks, then writes the
+superblock and issues another flush.
++
+The write flushes incur a slight hit and also prevent the IO block
+scheduler to reorder requests in more effective way. Disabling barriers gets
+rid of that penalty but will most certainly lead to a corrupted filesystem in
+case of a crash or power loss. The ordinary metadata blocks could be yet
+unwrittent at the time the new superblock is stored permanently, expecting that
+the block pointers to metadata were stored permanently before.
++
+On a device with a volatile battery-backed write-back cache, the 'nobarrier'
+option will not lead to filesystem corruption as the pending blocks are
+supposed to make it to the permanent storage.
+
+*check_int*::
+*check_int_data*::
+*check_int_print_mask='value'*::
+(since: 3.0, default: off)
++
+These debugging options control the behavior of the integrity checking
+module (the BTRFS_FS_CHECK_INTEGRITY config option required). +
++
+`check_int` enables the integrity checker module, which examines all
+block write requests to ensure on-disk consistency, at a large
+memory and CPU cost. +
++
+`check_int_data` includes extent data in the integrity checks, and
+implies the check_int option. +
++
+`check_int_print_mask` takes a bitmask of BTRFSIC_PRINT_MASK_* values
+as defined in 'fs/btrfs/check-integrity.c', to control the integrity
+checker module behavior. +
++
+See comments at the top of 'fs/btrfs/check-integrity.c'
+for more info.
+
+*clear_cache*::
+Force clearing and rebuilding of the disk space cache if something
+has gone wrong. See also: 'space_cache'.
+
+*commit='seconds'*::
+(since: 3.12, default: 30)
++
+Set the interval of periodic commit. Higher
+values defer data being synced to permanent storage with obvious
+consequences when the system crashes. The upper bound is not forced,
+but a warning is printed if it's more than 300 seconds (5 minutes).
+
+*compress*::
+*compress='type'*::
+*compress-force*::
+*compress-force='type'*::
+(default: off)
++
+Control BTRFS file data compression. Type may be specified as 'zlib',
+'lzo' or 'no' (for no compression, used for remounting). If no type
+is specified, 'zlib' is used. If compress-force is specified,
+all files will be compressed, whether or not they compress well.
++
+NOTE: If compression is enabled, 'nodatacow' and 'nodatasum' are disabled.
+
+*datacow*::
+*nodatacow*::
+(default: on)
++
+Enable data copy-on-write for newly created files.
+'Nodatacow' implies 'nodatasum', and disables 'compression'. All files created
+under 'nodatacow' are also set the NOCOW file attribute (see `chattr`(1)).
+
+*datasum*::
+*nodatasum*::
+(default: on)
++
+Enable data checksumming for newly created files.
+'Datasum' implies 'datacow', ie. the normal mode of operation. All files created
+under 'nodatasum' inherit the "no checksums" property, however there's no
+corresponding file attribute (see `chattr`(1)).
+
+*degraded*::
+(default: off)
++
+Allow mounts with less devices than the raid profile constraints
+require. A read-write mount (or remount) may fail with too many devices
+missing, for example if a stripe member is completely missing from RAID0.
+
+*device='devicepath'*::
+Specify a path to a device that will be scanned for BTRFS filesystem during
+mount. This is usually done automatically by a device manager (like udev) or
+using the *btrfs device scan* command (eg. run from the initial ramdisk). In
+cases where this is not possible the 'device' mount option can help.
++
+NOTE: booting eg. a RAID1 system may fail even if all filesystem's 'device'
+paths are provided as the actual device nodes may not be discovered by the
+system at that point.
+
+*discard*::
+*nodiscard*::
+(default: off)
++
+Enable discarding of freed file blocks using TRIM operation. This is useful
+for SSD devices, thinly provisioned LUNs or virtual machine images where the
+backing device understands the operation. Depending on support of the
+underlying device, the operation may severly hurt performance in case the TRIM
+operation is synchronous (eg. with SATA devices up to revision 3.0).
++
+If discarding is not necessary to be done at the block freeing time, there's
+*fstrim* tool that lets the filesystem discard all free blocks in a batch,
+possibly not much interfering with other operations.
+
+*enospc_debug*::
+*noenospc_debug*::
+(default: off)
++
+Enable verbose output for some ENOSPC conditions. It's safe to use but can
+be noisy if the system hits reaches near-full state.
+
+*fatal_errors='action'*::
+(since: 3.4, default: bug)
++
+Action to take when encountering a fatal error.
++
+*bug*::::
+'BUG()' on a fatal error, the system will stay in the crashed state and may be
+still partially usable, but reboot is required for full operation
++
+*panic*::::
+'panic()' on a fatal error, depending on other system configuration, this may
+be followed by a reboot. Please refer to the documentation of kernel boot
+parameters, eg. 'panic', 'oops' or 'crashkernel'.
+
+*flushoncommit*::
+*noflushoncommit*::
+(default: on)
++
+This option forces any data dirtied by a write in a prior transaction to commit
+as part of the current commit. This makes the committed state a fully
+consistent view of the file system from the application's perspective (i.e., it
+includes all completed file system operations). This was previously the
+behavior only when a snapshot was created.
++
+Disabling flushing may improve performance but is not crash-safe.
+
+*fragment='type'*::
+(depends on compile-time option BTRFS_DEBUG, since: 4.4, default: off)
++
+A debugging helper to intentionally fragment given 'type' of block groups. The
+type can be 'data', 'metadata' or 'all'. This mount option should not be used
+outside of debugging environments and is not recognized if the kernel config
+option 'BTRFS_DEBUG' is not enabled.
+
+*inode_cache*::
+*noinode_cache*::
+(since: 3.0, default: off)
++
+Enable free inode number caching. Not recommended to use unless files on your
+filesystem get assigned inode numbers that are approaching 2^64^. Normally, new
+files in each subvolume get assigned incrementally (plus one from the last
+time) and are not reused. The mount option turns on caching of the existing
+inode numbers and reuse of inode numbers of deleted files.
++
+This option may slow down your system at first run, or after mounting without
+the option.
++
+NOTE: Defaults to off due to a potential overflow problem when the free space
+checksums don't fit inside a single page.
+
+*max_inline='bytes'*::
+(default: min(8192, page size) )
++
+Specify the maximum amount of space, in bytes, that can be inlined in
+a metadata B-tree leaf. The value is specified in bytes, optionally
+with a K suffix (case insensitive). In practice, this value
+is limited by the filesystem block size (named 'sectorsize' at mkfs time),
+and memory page size of the system. In case of sectorsize limit, there's
+some space unavailable due to leaf headers. For example, a 4k sectorsize, max
+inline data is ~3900 bytes.
++
+Inlining can be completely turned off specifying 0. This will increase data
+block slack if file sizes are much smaller than block size but will reduce
+metadata consumption in return.
+
+*metadata_ratio='value'*::
+(default: 0, internal logic)
++
+Specifies that 1 metadata chunk should be allocated after every 'value' data
+chunks. Default behaviour depends on internal logic, some percent of unused
+metadata space is attempted to be maintained but is not always possible if
+there's not space left for chunk allocation. The option could be useful to
+override the internal logic in favor of the metadata allocation if the expected
+workload is supposed to be metadata intense (snapshots, reflinks, xattrs,
+inlined files).
+
+*recovery*::
+(since: 3.2, default: off)
++
+Enable autorecovery attempts if a bad tree root is found at mount time.
+Currently this scans a backup list of several previous tree roots and tries to
+use the first readable. This can be used with read-only mounts as well.
+
+*rescan_uuid_tree*::
+(since: 3.12, default: off)
++
+Force check and rebuild procedure of the UUID tree. This should not
+normally be needed.
+
+*skip_balance*::
+(since: 3.3, default: off)
++
+Skip automatic resume of interrupted balance operation after mount.
+May be resumed with *btrfs balance resume* or the paused state can be removed
+by *btrfs balance cancel*.
+
+*space_cache*::
+*nospace_cache*::
+('nospace_cache' since: 3.2, default: on)
++
+Disable freespace cache loading without clearing the cache and the free space
+cache will not be used during the mount. This affects performance as searching
+for new free blocks could take longer. On the other hand, managing the space
+cache consumes some resources.
+
+*ssd*::
+*nossd*::
+*ssd_spread*::
+(default: SSD autodetected)
++
+Options to control SSD allocation schemes. By default, BTRFS will
+enable or disable SSD allocation heuristics depending on whether a
+rotational or nonrotational disk is in use. The 'ssd' and 'nossd' options
+can override this autodetection.
++
+The 'ssd_spread' mount option attempts to allocate into bigger and aligned
+chunks of unused space, and may perform better on low-end SSDs. 'ssd_spread'
+implies 'ssd', enabling all other SSD heuristics as well.
+
+*subvol='path'*::
+Mount subvolume from 'path' rather than the toplevel subvolume. The
+'path' is absolute (ie. starts at the toplevel subvolume).
+This mount option overrides the default subvolume set for the given filesystem.
+
+*subvolid='subvolid'*::
+Mount subvolume specified by a 'subvolid' number rather than the toplevel
+subvolume. You can use *btrfs subvolume list* to see subvolume ID numbers.
+This mount option overrides the default subvolume set for the given filesystem.
+
+*subvolrootid='objectid'*::
+(irrelevant since: 3.2, formally deprecated since: 3.10)
++
+A workaround option from times (pre 3.2) when it was not possible to mount a
+subvolume that did not reside directly under the toplevel subvolume.
+
+*thread_pool='number'*::
+(default: min(NRCPUS + 2, 8) )
++
+The number of worker threads to allocate. NRCPUS is number of on-line CPUs
+detected at the time of mount. Small number leads to less parallelism in
+processing data and metadata, higher numbers could lead to a performance due to
+increased locking contention, cache-line bouncing or costly data transfers
+between local CPU memories.
+
+*treelog*::
+*notreelog*::
+(default: on)
++
+Enable the tree logging used for 'fsync' and 'O_SYNC' writes. The tree log
+stores changes without the need of a full filesystem sync. The log operations
+are flushed at sync and transaction commit. If the system crashes between two
+such syncs, the pending tree log operations are replayed during mount.
++
+WARNING: currently, the tree log is replayed even with a read-only mount!
++
+The tree log could contain new files/directories, these would not exist on
+a mounted filesystm if the log is not replayed.
+
+*user_subvol_rm_allowed*::
+(default: off)
++
+Allow subvolumes to be deleted by their respective owner. Otherwise, only the
+root user can do that.
+
+FILE ATTRIBUTES
+---------------
+The btrfs filesystem supports setting the following file attributes using the
+`chattr`(1) utility:
+
+*a*::
+'append only', new writes are always written at the end of the file
+
+*A*::
+'no atime updates'
+
+*c*::
+'compress data', all data written after this attribute is set will be compressed.
+Please note that compression is also affected by the mount options or the parent
+directory attributes.
++
+When set on a directory, all newly created files will inherit this attribute.
+
+*C*::
+'no copy-on-write', file modifications are done in-place
++
+When set on a directory, all newly created files will inherit this attribute.
++
+NOTE: due to implementation limitations, this flag can be set/unset only on
+empty files.
+
+*d*::
+'no dump', makes sense with 3rd party tools like `dump`(8), on BTRFS the
+attribute can be set/unset on no other special handling is done
+
+*D*::
+'synchronous directory updates', for more details search `open`(2) for 'O_SYNC'
+and 'O_DSYNC'
+
+*i*::
+'immutable', no file data and metadata changes allowed even to the root user as
+long as this attribute is set (obviously the exception is unsetting the attribute)
+
+*S*::
+'synchronous updates', for more details search `open`(2) for 'O_SYNC' and
+'O_DSYNC'
+
+*X*::
+'no compression', permanently turn off compression on the given file, other
+compression mount options will not affect that
++
+When set on a directory, all newly created files will inherit this attribute.
+
+No other attributes are supported. For the complete list please refer to the
+`chattr`(1) manual page.
+
+SEE ALSO
+--------
+`acl`(5),
+`btrfs`(8),
+`chattr`(1),
+`fstrim`(8),
+`mkfs.btrfs`(8),
+`mount`(8)
diff --git a/Documentation/btrfs-mount.asciidoc b/Documentation/btrfs-mount.asciidoc
deleted file mode 100644
index 39215a8..0000000
--- a/Documentation/btrfs-mount.asciidoc
+++ /dev/null
@@ -1,251 +0,0 @@
-btrfs-mount(5)
-==============
-
-NAME
-----
-btrfs-mount - mount options and supported file attributes for the btrfs filesystem
-
-DESCRIPTION
------------
-This document describes mount options specific to the btrfs filesystem.
-Other generic mount options are available,and are described in the
-`mount`(8) manpage.
-
-MOUNT OPTIONS
--------------
-*alloc_start='bytes'*::
- Debugging option to force all block allocations above a certain
- byte threshold on each block device. The value is specified in
- bytes, optionally with a K, M, or G suffix, case insensitive.
- Default is 1MB.
-
-*autodefrag*::
-*noautodefrag*::
- (since: 3.0, default: off) +
- Disable/enable auto defragmentation.
- Auto defragmentation detects small random writes into files and queue
- them up for the defrag process. Works best for small files;
- Not well suited for large database workloads.
-
-*check_int*::
-*check_int_data*::
-*check_int_print_mask='value'*::
- (since: 3.0, default: off) +
- These debugging options control the behavior of the integrity checking
- module (the BTRFS_FS_CHECK_INTEGRITY config option required). +
- +
- `check_int` enables the integrity checker module, which examines all
- block write requests to ensure on-disk consistency, at a large
- memory and CPU cost. +
- +
- `check_int_data` includes extent data in the integrity checks, and
- implies the check_int option. +
- +
- `check_int_print_mask` takes a bitmask of BTRFSIC_PRINT_MASK_* values
- as defined in 'fs/btrfs/check-integrity.c', to control the integrity
- checker module behavior. +
- +
- See comments at the top of 'fs/btrfs/check-integrity.c'
- for more info.
-
-*commit='seconds'*::
- (since: 3.12, default: 30) +
- Set the interval of periodic commit. Higher
- values defer data being synced to permanent storage with obvious
- consequences when the system crashes. The upper bound is not forced,
- but a warning is printed if it's more than 300 seconds (5 minutes).
-
-*compress*::
-*compress='type'*::
-*compress-force*::
-*compress-force='type'*::
- (default: off) +
- Control BTRFS file data compression. Type may be specified as 'zlib',
- 'lzo' or 'no' (for no compression, used for remounting). If no type
- is specified, 'zlib' is used. If compress-force is specified,
- all files will be compressed, whether or not they compress well.
- NOTE: If compression is enabled, 'nodatacow' and 'nodatasum' are disabled.
-
-*degraded*::
- (default: off) +
- Allow mounts to continue with missing devices. A read-write mount may
- fail with too many devices missing, for example if a stripe member
- is completely missing.
-
-*device='devicepath'*::
- Specify a device during mount so that ioctls on the control device
- can be avoided. Especially useful when trying to mount a multi-device
- setup as root. May be specified multiple times for multiple devices.
-
-*discard*::
-*nodiscard*::
- (default: off) +
- Disable/enable discard mount option.
- Discard issues frequent commands to let the block device reclaim space
- freed by the filesystem.
- This is useful for SSD devices, thinly provisioned
- LUNs and virtual machine images, but may have a significant
- performance impact. (The fstrim command is also available to
- initiate batch trims from userspace).
-
-*enospc_debug*::
- (default: off) +
- Disable/enable debugging option to be more verbose in some ENOSPC conditions.
-
-*fatal_errors='action'*::
- (since: 3.4, default: bug) +
- Action to take when encountering a fatal error. +
- "bug" - BUG() on a fatal error. +
- "panic" - panic() on a fatal error.
-
-*flushoncommit*::
-*noflushoncommit*::
- (default: on) +
- The `flushoncommit` mount option forces any data dirtied by a write in a
- prior transaction to commit as part of the current commit. This makes
- the committed state a fully consistent view of the file system from the
- application's perspective (i.e., it includes all completed file system
- operations). This was previously the behavior only when a snapshot is
- created.
-
-*inode_cache*::
-*noinode_cache*::
- (since: 3.0, default: off) +
- Enable free inode number caching. Defaults to off due to an overflow
- problem when the free space crcs don't fit inside a single page.
-
-*max_inline='bytes'*::
- (default: min(8192, page size) )
- Specify the maximum amount of space, in bytes, that can be inlined in
- a metadata B-tree leaf. The value is specified in bytes, optionally
- with a K, M, or G suffix, case insensitive. In practice, this value
- is limited by the root sector size, with some space unavailable due
- to leaf headers. For a 4k sectorsize, max inline data is ~3900 bytes.
-
-*metadata_ratio='value'*::
- Specify that 1 metadata chunk should be allocated after every
- 'value' data chunks. Off by default.
-
-*acl*::
-*noacl*::
- (default: on) +
- Enable/disable support for Posix Access Control Lists (ACLs). See the
- `acl`(5) manual page for more information about ACLs.
-
-*barrier*::
-*nobarrier*::
- (default: on) +
- ensure that certain IOs make it through the device cache and are on
- persistent storage. If disabled on a device with a volatile
- (non-battery-backed) write-back cache, nobarrier option will lead to
- filesystem corruption on a system crash or power loss.
-
-*datacow*::
-*nodatacow*::
- (default: on) +
- Enable/disable data copy-on-write for newly created files.
- Nodatacow implies nodatasum, and disables all compression.
-
-*datasum*::
-*nodatasum*::
- (default: on) +
- Enable/disable data checksumming for newly created files.
- Datasum implies datacow.
-
-*treelog*::
-*notreelog*::
- (default: on) +
- Enable/disable the tree logging used for fsync and O_SYNC writes.
-
-*recovery*::
- (since: 3.2, default: off) +
- Enable autorecovery attempts if a bad tree root is found at mount time.
- Currently this scans a list of several previous tree roots and tries to
- use the first readable.
-
-*rescan_uuid_tree*::
- (since: 3.12, default: off) +
- Force check and rebuild procedure of the UUID tree. This should not
- normally be needed.
-
-*skip_balance*::
- (since: 3.3, default: off) +
- Skip automatic resume of interrupted balance operation after mount.
- May be resumed with "btrfs balance resume."
-
-*nospace_cache*::
- (since: 3.2) +
- Disable freespace cache loading without clearing the cache.
-
-*clear_cache*::
- Force clearing and rebuilding of the disk space cache if something
- has gone wrong.
-
-*ssd*::
-*nossd*::
-*ssd_spread*::
- Options to control ssd allocation schemes. By default, BTRFS will
- enable or disable ssd allocation heuristics depending on whether a
- rotational or nonrotational disk is in use. The ssd and nossd options
- can override this autodetection. +
- The ssd_spread mount option attempts to allocate into big chunks
- of unused space, and may perform better on low-end ssds. ssd_spread
- implies ssd, enabling all other ssd heuristics as well.
-
-*subvol='path'*::
- Mount subvolume at 'path' rather than the root subvolume. The
- 'path' is relative to the top level subvolume.
-
-*subvolid='ID'*::
- Mount subvolume specified by an ID number rather than the root subvolume.
- This allows mounting of subvolumes which are not in the root of the mounted
- filesystem.
- You can use "btrfs subvolume list" to see subvolume ID numbers.
-
-*subvolrootid='objectid'*::
- (deprecated) +
- Mount subvolume specified by 'objectid' rather than the root subvolume.
- This allows mounting of subvolumes which are not in the root of the mounted
- filesystem.
- You can use "btrfs subvolume show" to see the object ID for a subvolume.
-
-*thread_pool='number'*::
- The number of worker threads to allocate. The default number is equal
- to the number of CPUs + 2, or 8, whichever is smaller.
-
-*user_subvol_rm_allowed*::
- (default: off) +
- Allow subvolumes to be deleted by a non-root user. Use with caution.
-
-FILE ATTRIBUTES
----------------
-The btrfs filesystem supports setting the following file
-attributes the `chattr`(1) utility
-
-*a* -- append only
-
-*A* -- no atime updates
-
-*c* -- compressed
-
-*C* -- no copy on write
-
-*d* -- no dump
-
-*D* -- synchronous directory updates
-
-*i* -- immutable
-
-*S* -- synchronous updates
-
-*X* -- no compression
-
-For descriptions of these attribute flags, please refer to the
-`chattr`(1) man page.
-
-SEE ALSO
---------
-`chattr`(1),
-`mkfs.btrfs`(8),
-`mount`(8),
-`btrfs`(8)
diff --git a/Documentation/btrfs.asciidoc b/Documentation/btrfs.asciidoc
index 3740c8b..abf1ff8 100644
--- a/Documentation/btrfs.asciidoc
+++ b/Documentation/btrfs.asciidoc
@@ -11,21 +11,27 @@ SYNOPSIS
DESCRIPTION
-----------
-*btrfs* is used to control the filesystem and the files and directories stored.
-It is the tool to create or destroy a snapshot or a subvolume for the
-filesystem, to defrag a file or a directory, flush the data to the disk,
-to resize the filesystem, to scan the device.
+The *btrfs* utility is a toolbox for managing btrfs filesystems. There are
+command groups to work with subvolumes, devices, for whole filesystem or other
+specific actions. See section *COMMANDS*.
+
+COMMAND SYTNAX
+--------------
+
+Any command name can be shortened as far as it stays unambiguous,
+however it is recommended to use full command names in scripts.
+All command groups have their manual page named *btrfs-<group>*.
-It is possible to abbreviate the commands unless the commands are ambiguous.
For example: it is possible to run *btrfs sub snaps* instead of
*btrfs subvolume snapshot*.
But *btrfs file s* is not allowed, because *file s* may be interpreted
both as *filesystem show* and as *filesystem sync*.
-If a command is terminated by '--help', the detailed help is showed.
-If the passed command matches more commands,
-detailed help of all the matched commands is showed. For example
-*btrfs dev --help* shows the help of all *device** commands.
+If the command name is ambiguous, the list of conflicting options is
+printed.
+
+For an overview of a given command use 'btrfs command --help'
+or 'btrfs [command...] --help --full' to print all available options.
COMMANDS
--------
diff --git a/Documentation/mkfs.btrfs.asciidoc b/Documentation/mkfs.btrfs.asciidoc
index 12d8840..0b145c7 100644
--- a/Documentation/mkfs.btrfs.asciidoc
+++ b/Documentation/mkfs.btrfs.asciidoc
@@ -50,7 +50,9 @@ mkfs.btrfs uses the entire device space 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).
+'raid1', 'raid5', 'raid6', 'raid10' or 'single' or dup (case does not matter).
++
+See 'DUP PROFILES ON A SINGLE DEVICE' for more.
*-m|--metadata <profile>*::
Specify the profile for the metadata block groups.
@@ -60,13 +62,12 @@ Valid values are 'raid0', 'raid1', 'raid5', 'raid6', 'raid10', 'single' or
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 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.
++
+See 'DUP PROFILES ON A SINGLE DEVICE' for more details.
*-M|--mixed*::
Normally the data and metadata block groups are isolated. The 'mixed' mode
@@ -248,17 +249,46 @@ PROFILES
There are the following block group types available:
-[ width="60%",options="header" ]
+[ cols="^,^,^,^,^",width="60%" ]
|=============================================================
-| 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
+.2+^.<h| Profile 3+^.^h| Redundancy .2+^.<h| Min/max devices
+ ^.^h| Copies ^.^h| Parity ^.<h| Striping
+| single | 1 | | | 1/any
+| DUP | 2 / 1 device | | | 1/1 ^(see note)^
+| RAID0 | | | 1 to N | 2/any
+| RAID1 | 2 | | | 2/any
+| RAID10 | 2 | | 1 to N | 4/any
+| RAID5 | 1 | 1 | 2 to N - 1 | 2/any
+| RAID6 | 1 | 2 | 3 to N - 2 | 3/any
|=============================================================
+'Note:' DUP may exist on more than 1 device if it starts on a single device and
+another one is added, but *mkfs.btrfs* will not let you create DUP on multiple
+devices.
+
+DUP PROFILES ON A SINGLE DEVICE
+-------------------------------
+
+The mkfs utility will let the user create a filesystem with profiles that write
+the logical blocks to 2 physical locations. Whether there are really 2
+physical copies highly depends on the underlying device type.
+
+For example, a SSD drive can remap the blocks internally to a single copy thus
+deduplicating them. This negates the purpose of increased redunancy and just
+wastes space.
+
+The duplicated data/metadata may still be useful to statistically improve the
+chances on a device that might perform some internal optimizations. The actual
+details are not usually disclosed by vendors. As another example, the widely
+used USB flash or SD cards use a translation layer. The data lifetime may
+be affected by frequent plugging. The memory cells could get damaged, hopefully
+not destroying both copies of particular data.
+
+The traditional rotational hard drives usually fail at the sector level.
+
+In any case, a device that starts to misbehave and repairs from the DUP copy
+should be replaced! *DUP is not backup*.
+
KNOWN ISSUES
------------
diff --git a/Makefile.in b/Makefile.in
index 8e24808..19697ff 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -42,7 +42,7 @@ CFLAGS = @CFLAGS@ \
-DBTRFS_FLAT_INCLUDES \
-D_XOPEN_SOURCE=700 \
-fno-strict-aliasing \
- -fPIC $(KBUILD_CFLAGS) $(EXTRA_CFLAGS)
+ -fPIC $(EXTRAWARN_CFLAGS) $(EXTRA_CFLAGS)
LDFLAGS = @LDFLAGS@ \
-rdynamic $(EXTRA_LDFLAGS)
@@ -60,6 +60,7 @@ STATIC_LIBS = @UUID_LIBS_STATIC@ @BLKID_LIBS_STATIC@ \
# generate so many sparse errors that sparse stops parsing,
# which masks real errors that we want to see.
CHECKER := sparse
+check_defs := .cc-defines.h
CHECKER_FLAGS := -include $(check_defs) -D__CHECKER__ \
-D__CHECK_ENDIAN__ -Wbitwise -Wuninitialized -Wshadow -Wundef \
-U_FORTIFY_SOURCE
@@ -69,7 +70,7 @@ objects = ctree.o disk-io.o radix-tree.o extent-tree.o print-tree.o \
extent-cache.o extent_io.o volumes.o utils.o repair.o \
qgroup.o raid6.o free-space-cache.o list_sort.o props.o \
ulist.o qgroup-verify.o backref.o string-table.o task-utils.o \
- inode.o file.o find-root.o
+ inode.o file.o find-root.o free-space-tree.o help.o
cmds_objects = cmds-subvolume.o cmds-filesystem.o cmds-device.o cmds-scrub.o \
cmds-inspect.o cmds-balance.o cmds-send.o cmds-receive.o \
cmds-quota.o cmds-qgroup.o cmds-replace.o cmds-check.o \
@@ -154,7 +155,6 @@ lib_links = libbtrfs.so.0 libbtrfs.so
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
@@ -260,14 +260,14 @@ btrfs-%: $(objects) $(libs_static) btrfs-%.o
$(Q)$(CC) $(CFLAGS) -o $@ $(objects) $@.o $(libs_static) \
$(LDFLAGS) $(LIBS) $($(subst -,_,$@-libs))
-btrfs: $(objects) btrfs.o help.o $(cmds_objects) $(libs_static)
+btrfs: $(objects) btrfs.o $(cmds_objects) $(libs_static)
@echo " [LD] $@"
- $(Q)$(CC) $(CFLAGS) -o btrfs btrfs.o help.o $(cmds_objects) \
+ $(Q)$(CC) $(CFLAGS) -o btrfs btrfs.o $(cmds_objects) \
$(objects) $(libs_static) $(LDFLAGS) $(LIBS)
-btrfs.static: $(static_objects) btrfs.static.o help.static.o $(static_cmds_objects) $(static_libbtrfs_objects)
+btrfs.static: $(static_objects) btrfs.static.o $(static_cmds_objects) $(static_libbtrfs_objects)
@echo " [LD] $@"
- $(Q)$(CC) $(STATIC_CFLAGS) -o btrfs.static btrfs.static.o help.static.o $(static_cmds_objects) \
+ $(Q)$(CC) $(STATIC_CFLAGS) -o btrfs.static btrfs.static.o $(static_cmds_objects) \
$(static_objects) $(static_libbtrfs_objects) $(STATIC_LDFLAGS) $(STATIC_LIBS)
# For backward compatibility, 'btrfs' changes behaviour to fsck if it's named 'btrfsck'
diff --git a/btrfs-calc-size.c b/btrfs-calc-size.c
index b84cda9..e3f02d8 100644
--- a/btrfs-calc-size.c
+++ b/btrfs-calc-size.c
@@ -63,11 +63,6 @@ struct root_stats {
int total_levels;
};
-struct fs_root {
- struct btrfs_key key;
- struct btrfs_key *snaps;
-};
-
static int add_seek(struct rb_root *root, u64 dist)
{
struct rb_node **p = &root->rb_node;
@@ -436,9 +431,7 @@ static void usage(void)
int main(int argc, char **argv)
{
struct btrfs_key key;
- struct fs_root *roots;
struct btrfs_root *root;
- size_t fs_roots_size = sizeof(struct fs_root);
int opt;
int ret = 0;
@@ -482,12 +475,6 @@ int main(int argc, char **argv)
exit(1);
}
- roots = malloc(fs_roots_size);
- if (!roots) {
- fprintf(stderr, "No memory\n");
- goto out;
- }
-
printf("Calculating size of root tree\n");
key.objectid = BTRFS_ROOT_TREE_OBJECTID;
ret = calc_root_size(root, &key, 0);
@@ -506,15 +493,14 @@ int main(int argc, char **argv)
if (ret)
goto out;
- roots[0].key.objectid = BTRFS_FS_TREE_OBJECTID;
- roots[0].key.offset = (u64)-1;
+ key.objectid = BTRFS_FS_TREE_OBJECTID;
+ key.offset = (u64)-1;
printf("Calculatin' size of fs tree\n");
- ret = calc_root_size(root, &roots[0].key, 1);
+ ret = calc_root_size(root, &key, 1);
if (ret)
goto out;
out:
close_ctree(root);
- free(roots);
btrfs_close_all_devices();
return ret;
}
diff --git a/btrfs-convert.c b/btrfs-convert.c
index 5b9171e..4baa68e 100644
--- a/btrfs-convert.c
+++ b/btrfs-convert.c
@@ -77,11 +77,108 @@ static int after_copied_inodes(void *p)
return 0;
}
+struct btrfs_convert_context;
+struct btrfs_convert_operations {
+ const char *name;
+ int (*open_fs)(struct btrfs_convert_context *cctx, const char *devname);
+ int (*alloc_block)(struct btrfs_convert_context *cctx, u64 goal,
+ u64 *block_ret);
+ int (*alloc_block_range)(struct btrfs_convert_context *cctx, u64 goal,
+ int num, u64 *block_ret);
+ int (*test_block)(struct btrfs_convert_context *cctx, u64 block);
+ void (*free_block)(struct btrfs_convert_context *cctx, u64 block);
+ void (*free_block_range)(struct btrfs_convert_context *cctx, u64 block,
+ int num);
+ int (*copy_inodes)(struct btrfs_convert_context *cctx,
+ struct btrfs_root *root, int datacsum,
+ int packing, int noxattr, struct task_ctx *p);
+ void (*close_fs)(struct btrfs_convert_context *cctx);
+};
+
+struct btrfs_convert_context {
+ u32 blocksize;
+ u32 first_data_block;
+ u32 block_count;
+ u32 inodes_count;
+ u32 free_inodes_count;
+ u64 total_bytes;
+ char *volume_name;
+ const struct btrfs_convert_operations *convert_ops;
+
+ /* The accurate used space of old filesystem */
+ struct cache_tree used;
+
+ /* Batched ranges which must be covered by data chunks */
+ struct cache_tree data_chunks;
+
+ /* Free space which is not covered by data_chunks */
+ struct cache_tree free;
+
+ void *fs_data;
+};
+
+static void init_convert_context(struct btrfs_convert_context *cctx)
+{
+ cache_tree_init(&cctx->used);
+ cache_tree_init(&cctx->data_chunks);
+ cache_tree_init(&cctx->free);
+}
+
+static void clean_convert_context(struct btrfs_convert_context *cctx)
+{
+ free_extent_cache_tree(&cctx->used);
+ free_extent_cache_tree(&cctx->data_chunks);
+ free_extent_cache_tree(&cctx->free);
+}
+
+static inline int convert_alloc_block(struct btrfs_convert_context *cctx,
+ u64 goal, u64 *ret)
+{
+ return cctx->convert_ops->alloc_block(cctx, goal, ret);
+}
+
+static inline int convert_alloc_block_range(struct btrfs_convert_context *cctx,
+ u64 goal, int num, u64 *ret)
+{
+ return cctx->convert_ops->alloc_block_range(cctx, goal, num, ret);
+}
+
+static inline int convert_test_block(struct btrfs_convert_context *cctx,
+ u64 block)
+{
+ return cctx->convert_ops->test_block(cctx, block);
+}
+
+static inline void convert_free_block(struct btrfs_convert_context *cctx,
+ u64 block)
+{
+ cctx->convert_ops->free_block(cctx, block);
+}
+
+static inline void convert_free_block_range(struct btrfs_convert_context *cctx,
+ u64 block, int num)
+{
+ cctx->convert_ops->free_block_range(cctx, block, num);
+}
+
+static inline int copy_inodes(struct btrfs_convert_context *cctx,
+ struct btrfs_root *root, int datacsum,
+ int packing, int noxattr, struct task_ctx *p)
+{
+ return cctx->convert_ops->copy_inodes(cctx, root, datacsum, packing,
+ noxattr, p);
+}
+
+static inline void convert_close_fs(struct btrfs_convert_context *cctx)
+{
+ cctx->convert_ops->close_fs(cctx);
+}
+
/*
* Open Ext2fs in readonly mode, read block allocation bitmap and
* inode bitmap into memory.
*/
-static int open_ext2fs(const char *name, ext2_filsys *ret_fs)
+static int ext2_open_fs(struct btrfs_convert_context *cctx, const char *name)
{
errcode_t ret;
ext2_filsys ext2_fs;
@@ -114,20 +211,38 @@ static int open_ext2fs(const char *name, ext2_filsys *ret_fs)
ino += EXT2_INODES_PER_GROUP(ext2_fs->super);
}
- *ret_fs = ext2_fs;
+ if (!(ext2_fs->super->s_feature_incompat &
+ EXT2_FEATURE_INCOMPAT_FILETYPE)) {
+ fprintf(stderr, "filetype feature is missing\n");
+ goto fail;
+ }
+
+ cctx->fs_data = ext2_fs;
+ cctx->blocksize = ext2_fs->blocksize;
+ cctx->block_count = ext2_fs->super->s_blocks_count;
+ cctx->total_bytes = ext2_fs->blocksize * ext2_fs->super->s_blocks_count;
+ cctx->volume_name = strndup(ext2_fs->super->s_volume_name, 16);
+ cctx->first_data_block = ext2_fs->super->s_first_data_block;
+ cctx->inodes_count = ext2_fs->super->s_inodes_count;
+ cctx->free_inodes_count = ext2_fs->super->s_free_inodes_count;
return 0;
fail:
return -1;
}
-static int close_ext2fs(ext2_filsys fs)
+static void ext2_close_fs(struct btrfs_convert_context *cctx)
{
- ext2fs_close(fs);
- return 0;
+ if (cctx->volume_name) {
+ free(cctx->volume_name);
+ cctx->volume_name = NULL;
+ }
+ ext2fs_close(cctx->fs_data);
}
-static int ext2_alloc_block(ext2_filsys fs, u64 goal, u64 *block_ret)
+static int ext2_alloc_block(struct btrfs_convert_context *cctx,
+ u64 goal, u64 *block_ret)
{
+ ext2_filsys fs = cctx->fs_data;
blk_t block;
if (!ext2fs_new_block(fs, goal, NULL, &block)) {
@@ -138,9 +253,10 @@ static int ext2_alloc_block(ext2_filsys fs, u64 goal, u64 *block_ret)
return -ENOSPC;
}
-static int ext2_alloc_block_range(ext2_filsys fs, u64 goal, int num,
- u64 *block_ret)
+static int ext2_alloc_block_range(struct btrfs_convert_context *cctx, u64 goal,
+ int num, u64 *block_ret)
{
+ ext2_filsys fs = cctx->fs_data;
blk_t block;
ext2fs_block_bitmap bitmap = fs->block_map;
blk_t start = ext2fs_get_block_bitmap_start(bitmap);
@@ -157,31 +273,34 @@ static int ext2_alloc_block_range(ext2_filsys fs, u64 goal, int num,
return -ENOSPC;
}
-static int ext2_free_block(ext2_filsys fs, u64 block)
+static void ext2_free_block(struct btrfs_convert_context *cctx, u64 block)
{
+ ext2_filsys fs = cctx->fs_data;
+
BUG_ON(block != (blk_t)block);
ext2fs_fast_unmark_block_bitmap(fs->block_map, block);
- return 0;
}
-static int ext2_free_block_range(ext2_filsys fs, u64 block, int num)
+static void ext2_free_block_range(struct btrfs_convert_context *cctx, u64 block, int num)
{
+ ext2_filsys fs = cctx->fs_data;
+
BUG_ON(block != (blk_t)block);
ext2fs_fast_unmark_block_bitmap_range(fs->block_map, block, num);
- return 0;
}
-static int cache_free_extents(struct btrfs_root *root, ext2_filsys ext2_fs)
+static int cache_free_extents(struct btrfs_root *root,
+ struct btrfs_convert_context *cctx)
{
int i, ret = 0;
blk_t block;
u64 bytenr;
- u64 blocksize = ext2_fs->blocksize;
+ u64 blocksize = cctx->blocksize;
- block = ext2_fs->super->s_first_data_block;
- for (; block < ext2_fs->super->s_blocks_count; block++) {
- if (ext2fs_fast_test_block_bitmap(ext2_fs->block_map, block))
+ block = cctx->first_data_block;
+ for (; block < cctx->block_count; block++) {
+ if (convert_test_block(cctx, block))
continue;
bytenr = block * blocksize;
ret = set_extent_dirty(&root->fs_info->free_space_cache,
@@ -192,7 +311,7 @@ static int cache_free_extents(struct btrfs_root *root, ext2_filsys ext2_fs)
for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) {
bytenr = btrfs_sb_offset(i);
bytenr &= ~((u64)BTRFS_STRIPE_LEN - 1);
- if (bytenr >= blocksize * ext2_fs->super->s_blocks_count)
+ if (bytenr >= blocksize * cctx->block_count)
break;
clear_extent_dirty(&root->fs_info->free_space_cache, bytenr,
bytenr + BTRFS_STRIPE_LEN - 1, 0);
@@ -1137,9 +1256,11 @@ fail:
/*
* scan ext2's inode bitmap and copy all used inodes.
*/
-static int copy_inodes(struct btrfs_root *root, ext2_filsys ext2_fs,
- int datacsum, int packing, int noxattr, struct task_ctx *p)
+static int ext2_copy_inodes(struct btrfs_convert_context *cctx,
+ struct btrfs_root *root,
+ int datacsum, int packing, int noxattr, struct task_ctx *p)
{
+ ext2_filsys ext2_fs = cctx->fs_data;
int ret;
errcode_t err;
ext2_inode_scan ext2_scan;
@@ -1191,6 +1312,14 @@ static int copy_inodes(struct btrfs_root *root, ext2_filsys ext2_fs,
return ret;
}
+static int ext2_test_block(struct btrfs_convert_context *cctx, u64 block)
+{
+ ext2_filsys ext2_fs = cctx->fs_data;
+
+ BUG_ON(block != (u32)block);
+ return ext2fs_fast_test_block_bitmap(ext2_fs->block_map, block);
+}
+
/*
* Construct a range of ext2fs image file.
* scan block allocation bitmap, find all blocks used by the ext2fs
@@ -1203,9 +1332,9 @@ static int create_image_file_range(struct btrfs_trans_handle *trans,
struct btrfs_root *root, u64 objectid,
struct btrfs_inode_item *inode,
u64 start_byte, u64 end_byte,
- ext2_filsys ext2_fs, int datacsum)
+ struct btrfs_convert_context *cctx, int datacsum)
{
- u32 blocksize = ext2_fs->blocksize;
+ u32 blocksize = cctx->blocksize;
u32 block = start_byte / blocksize;
u32 last_block = (end_byte + blocksize - 1) / blocksize;
int ret = 0;
@@ -1215,7 +1344,7 @@ static int create_image_file_range(struct btrfs_trans_handle *trans,
data.first_block = block;
for (; start_byte < end_byte; block++, start_byte += blocksize) {
- if (!ext2fs_fast_test_block_bitmap(ext2_fs->block_map, block))
+ if (!convert_test_block(cctx, block))
continue;
ret = block_iterate_proc(block, block, &data);
if (ret < 0)
@@ -1238,10 +1367,10 @@ fail:
return ret;
}
/*
- * Create the ext2fs image file.
+ * Create the fs image file.
*/
-static int create_ext2_image(struct btrfs_root *root, ext2_filsys ext2_fs,
- const char *name, int datacsum)
+static int create_image(struct btrfs_convert_context *cctx,
+ struct btrfs_root *root, const char *name, int datacsum)
{
int ret;
struct btrfs_key key;
@@ -1362,7 +1491,7 @@ next:
if (bytenr > last_byte) {
ret = create_image_file_range(trans, root, objectid,
&btrfs_inode, last_byte,
- bytenr, ext2_fs,
+ bytenr, cctx,
datacsum);
if (ret)
goto fail;
@@ -1386,7 +1515,7 @@ next:
if (total_bytes > last_byte) {
ret = create_image_file_range(trans, root, objectid,
&btrfs_inode, last_byte,
- total_bytes, ext2_fs,
+ total_bytes, cctx,
datacsum);
if (ret)
goto fail;
@@ -1449,7 +1578,7 @@ static struct btrfs_root * link_subvol(struct btrfs_root *root,
int ret;
len = strlen(base);
- if (len < 1 || len > BTRFS_NAME_LEN)
+ if (len == 0 || len > BTRFS_NAME_LEN)
return NULL;
path = btrfs_alloc_path();
@@ -1726,7 +1855,7 @@ static int init_btrfs(struct btrfs_root *root)
btrfs_set_root_dirid(&fs_info->fs_root->root_item,
BTRFS_FIRST_FREE_OBJECTID);
- /* subvol for ext2 image file */
+ /* subvol for fs image file */
ret = create_subvol(trans, root, CONV_IMAGE_SUBVOL_OBJECTID);
BUG_ON(ret);
/* subvol for data relocation */
@@ -2286,6 +2415,42 @@ err:
return ret;
}
+static const struct btrfs_convert_operations ext2_convert_ops = {
+ .name = "ext2",
+ .open_fs = ext2_open_fs,
+ .alloc_block = ext2_alloc_block,
+ .alloc_block_range = ext2_alloc_block_range,
+ .copy_inodes = ext2_copy_inodes,
+ .test_block = ext2_test_block,
+ .free_block = ext2_free_block,
+ .free_block_range = ext2_free_block_range,
+ .close_fs = ext2_close_fs,
+};
+
+static const struct btrfs_convert_operations *convert_operations[] = {
+ &ext2_convert_ops,
+};
+
+static int convert_open_fs(const char *devname,
+ struct btrfs_convert_context *cctx)
+{
+ int i;
+
+ memset(cctx, 0, sizeof(*cctx));
+
+ for (i = 0; i < ARRAY_SIZE(convert_operations); i++) {
+ int ret = convert_operations[i]->open_fs(cctx, devname);
+
+ if (ret == 0) {
+ cctx->convert_ops = convert_operations[i];
+ return ret;
+ }
+ }
+
+ fprintf(stderr, "No file system found to convert.\n");
+ return -1;
+}
+
static int do_convert(const char *devname, int datacsum, int packing, int noxattr,
u32 nodesize, int copylabel, const char *fslabel, int progress,
u64 features)
@@ -2297,38 +2462,34 @@ static int do_convert(const char *devname, int datacsum, int packing, int noxatt
u64 blocks[7];
u64 total_bytes;
u64 super_bytenr;
- ext2_filsys ext2_fs;
struct btrfs_root *root;
struct btrfs_root *image_root;
+ struct btrfs_convert_context cctx;
+ char *subvol_name = NULL;
struct task_ctx ctx;
char features_buf[64];
struct btrfs_mkfs_config mkfs_cfg;
- ret = open_ext2fs(devname, &ext2_fs);
- if (ret) {
- fprintf(stderr, "unable to open the Ext2fs\n");
+ init_convert_context(&cctx);
+ ret = convert_open_fs(devname, &cctx);
+ if (ret)
goto fail;
- }
- blocksize = ext2_fs->blocksize;
- total_bytes = (u64)ext2_fs->super->s_blocks_count * blocksize;
+
+ blocksize = cctx.blocksize;
+ total_bytes = (u64)blocksize * (u64)cctx.block_count;
if (blocksize < 4096) {
fprintf(stderr, "block size is too small\n");
goto fail;
}
- if (!(ext2_fs->super->s_feature_incompat &
- EXT2_FEATURE_INCOMPAT_FILETYPE)) {
- fprintf(stderr, "filetype feature is missing\n");
- goto fail;
- }
if (btrfs_check_nodesize(nodesize, blocksize, features))
goto fail;
blocks_per_node = nodesize / blocksize;
ret = -blocks_per_node;
for (i = 0; i < 7; i++) {
if (nodesize == blocksize)
- ret = ext2_alloc_block(ext2_fs, 0, blocks + i);
+ ret = convert_alloc_block(&cctx, 0, blocks + i);
else
- ret = ext2_alloc_block_range(ext2_fs,
+ ret = convert_alloc_block_range(&cctx,
ret + blocks_per_node, blocks_per_node,
blocks + i);
if (ret) {
@@ -2352,7 +2513,7 @@ static int do_convert(const char *devname, int datacsum, int packing, int noxatt
printf("\tnodesize: %u\n", nodesize);
printf("\tfeatures: %s\n", features_buf);
- mkfs_cfg.label = ext2_fs->super->s_volume_name;
+ mkfs_cfg.label = cctx.volume_name;
mkfs_cfg.fs_uuid = NULL;
memcpy(mkfs_cfg.blocks, blocks, sizeof(blocks));
mkfs_cfg.num_bytes = total_bytes;
@@ -2378,7 +2539,7 @@ static int do_convert(const char *devname, int datacsum, int packing, int noxatt
fprintf(stderr, "unable to open ctree\n");
goto fail;
}
- ret = cache_free_extents(root, ext2_fs);
+ ret = cache_free_extents(root, &cctx);
if (ret) {
fprintf(stderr, "error during cache_free_extents %d\n", ret);
goto fail;
@@ -2388,9 +2549,9 @@ static int do_convert(const char *devname, int datacsum, int packing, int noxatt
for (i = 0; i < 7; i++) {
blocks[i] /= blocksize;
if (nodesize == blocksize)
- ext2_free_block(ext2_fs, blocks[i]);
+ convert_free_block(&cctx, blocks[i]);
else
- ext2_free_block_range(ext2_fs, blocks[i],
+ convert_free_block_range(&cctx, blocks[i],
blocks_per_node);
}
ret = init_btrfs(root);
@@ -2399,15 +2560,14 @@ static int do_convert(const char *devname, int datacsum, int packing, int noxatt
goto fail;
}
printf("creating btrfs metadata.\n");
- ctx.max_copy_inodes = (ext2_fs->super->s_inodes_count
- - ext2_fs->super->s_free_inodes_count);
+ ctx.max_copy_inodes = (cctx.inodes_count - cctx.free_inodes_count);
ctx.cur_copy_inodes = 0;
if (progress) {
ctx.info = task_init(print_copied_inodes, after_copied_inodes, &ctx);
task_start(ctx.info);
}
- ret = copy_inodes(root, ext2_fs, datacsum, packing, noxattr, &ctx);
+ ret = copy_inodes(&cctx, root, datacsum, packing, noxattr, &ctx);
if (ret) {
fprintf(stderr, "error during copy_inodes %d\n", ret);
goto fail;
@@ -2416,21 +2576,32 @@ static int do_convert(const char *devname, int datacsum, int packing, int noxatt
task_stop(ctx.info);
task_deinit(ctx.info);
}
- printf("creating ext2fs image file.\n");
- image_root = link_subvol(root, "ext2_saved", CONV_IMAGE_SUBVOL_OBJECTID);
+
+ printf("creating %s image file.\n", cctx.convert_ops->name);
+ ret = asprintf(&subvol_name, "%s_saved", cctx.convert_ops->name);
+ if (ret < 0) {
+ fprintf(stderr, "error allocating subvolume name: %s_saved\n",
+ cctx.convert_ops->name);
+ goto fail;
+ }
+
+ image_root = link_subvol(root, subvol_name, CONV_IMAGE_SUBVOL_OBJECTID);
+
+ free(subvol_name);
+
if (!image_root) {
fprintf(stderr, "unable to create subvol\n");
goto fail;
}
- ret = create_ext2_image(image_root, ext2_fs, "image", datacsum);
+ ret = create_image(&cctx, image_root, "image", datacsum);
if (ret) {
- fprintf(stderr, "error during create_ext2_image %d\n", ret);
+ fprintf(stderr, "error during create_image %d\n", ret);
goto fail;
}
memset(root->fs_info->super_copy->label, 0, BTRFS_LABEL_SIZE);
if (copylabel == 1) {
strncpy(root->fs_info->super_copy->label,
- ext2_fs->super->s_volume_name, 16);
+ cctx.volume_name, BTRFS_LABEL_SIZE);
fprintf(stderr, "copy label '%s'\n",
root->fs_info->super_copy->label);
} else if (copylabel == -1) {
@@ -2449,11 +2620,12 @@ static int do_convert(const char *devname, int datacsum, int packing, int noxatt
fprintf(stderr, "error during close_ctree %d\n", ret);
goto fail;
}
- close_ext2fs(ext2_fs);
+ convert_close_fs(&cctx);
+ clean_convert_context(&cctx);
/*
* If this step succeed, we get a mountable btrfs. Otherwise
- * the ext2fs is left unchanged.
+ * the source fs is left unchanged.
*/
ret = migrate_super_block(fd, super_bytenr, blocksize);
if (ret) {
@@ -2479,6 +2651,7 @@ static int do_convert(const char *devname, int datacsum, int packing, int noxatt
printf("conversion complete.\n");
return 0;
fail:
+ clean_convert_context(&cctx);
if (fd != -1)
close(fd);
if (is_btrfs)
@@ -2875,7 +3048,7 @@ static void print_usage(void)
printf("\t-i|--no-xattr ignore xattrs and ACLs\n");
printf("\t-n|--no-inline disable inlining of small files to metadata\n");
printf("\t-N|--nodesize SIZE set filesystem metadata nodesize\n");
- printf("\t-r|--rollback roll back to ext2fs\n");
+ printf("\t-r|--rollback roll back to the original filesystem\n");
printf("\t-l|--label LABEL set filesystem label\n");
printf("\t-L|--copy-label use label from converted filesystem\n");
printf("\t-p|--progress show converting progress (default)\n");
diff --git a/btrfs-corrupt-block.c b/btrfs-corrupt-block.c
index ee2093f..c908b7e 100644
--- a/btrfs-corrupt-block.c
+++ b/btrfs-corrupt-block.c
@@ -21,6 +21,7 @@
#include <fcntl.h>
#include <unistd.h>
#include <getopt.h>
+#include <limits.h>
#include "kerncompat.h"
#include "ctree.h"
@@ -475,7 +476,7 @@ static int corrupt_dir_item(struct btrfs_root *root, struct btrfs_key *key,
struct btrfs_trans_handle *trans;
struct btrfs_dir_item *di;
struct btrfs_path *path;
- char *name;
+ char name[PATH_MAX];
struct btrfs_key location;
struct btrfs_disk_key disk_key;
unsigned long name_ptr;
@@ -514,17 +515,11 @@ static int corrupt_dir_item(struct btrfs_root *root, struct btrfs_key *key,
switch (corrupt_field) {
case BTRFS_DIR_ITEM_NAME:
name_len = btrfs_dir_name_len(path->nodes[0], di);
- name = malloc(name_len);
- if (!name) {
- ret = -ENOMEM;
- goto out;
- }
name_ptr = (unsigned long)(di + 1);
read_extent_buffer(path->nodes[0], name, name_ptr, name_len);
name[0]++;
write_extent_buffer(path->nodes[0], name, name_ptr, name_len);
btrfs_mark_buffer_dirty(path->nodes[0]);
- free(name);
goto out;
case BTRFS_DIR_ITEM_LOCATION_OBJECTID:
btrfs_dir_item_key_to_cpu(path->nodes[0], di, &location);
@@ -1217,7 +1212,7 @@ int main(int ac, char **av)
if (inode) {
struct btrfs_trans_handle *trans;
- if (!strlen(field))
+ if (*field == 0)
print_usage(1);
trans = btrfs_start_transaction(root, 1);
@@ -1233,13 +1228,13 @@ int main(int ac, char **av)
goto out_close;
}
if (metadata_block) {
- if (!strlen(field))
+ if (*field == 0)
print_usage(1);
ret = corrupt_metadata_block(root, metadata_block, field);
goto out_close;
}
if (corrupt_di) {
- if (!key.objectid || !strlen(field))
+ if (!key.objectid || *field == 0)
print_usage(1);
ret = corrupt_dir_item(root, &key, field);
goto out_close;
@@ -1276,7 +1271,7 @@ int main(int ac, char **av)
goto out_close;
}
if (key.objectid || key.offset || key.type) {
- if (!strlen(field))
+ if (*field == 0)
print_usage(1);
ret = corrupt_key(root, &key, field);
goto out_close;
diff --git a/btrfs-debug-tree.c b/btrfs-debug-tree.c
index 8adc39f..266176f 100644
--- a/btrfs-debug-tree.c
+++ b/btrfs-debug-tree.c
@@ -263,6 +263,29 @@ again:
if (!extent_buffer_uptodate(tree_root_scan->node))
goto no_node;
+ /*
+ * Tree's that are not pointed by the tree of tree roots
+ */
+ if (tree_id && tree_id == BTRFS_ROOT_TREE_OBJECTID) {
+ if (!info->tree_root->node) {
+ error("cannot print root tree, invalid pointer");
+ goto no_node;
+ }
+ printf("root tree\n");
+ btrfs_print_tree(info->tree_root, info->tree_root->node, 1);
+ goto no_node;
+ }
+
+ if (tree_id && tree_id == BTRFS_CHUNK_TREE_OBJECTID) {
+ if (!info->chunk_root->node) {
+ error("cannot print chunk tree, invalid pointer");
+ goto no_node;
+ }
+ printf("chunk tree\n");
+ btrfs_print_tree(info->chunk_root, info->chunk_root->node, 1);
+ goto no_node;
+ }
+
key.offset = 0;
key.objectid = 0;
btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY);
@@ -376,6 +399,10 @@ again:
if (!skip)
printf("uuid");
break;
+ case BTRFS_FREE_SPACE_TREE_OBJECTID:
+ if (!skip)
+ printf("free space");
+ break;
case BTRFS_MULTIPLE_OBJECTIDS:
if (!skip) {
printf("multiple");
diff --git a/btrfs-image.c b/btrfs-image.c
index d1d3d07..c7fa18f 100644
--- a/btrfs-image.c
+++ b/btrfs-image.c
@@ -733,39 +733,33 @@ static int metadump_init(struct metadump_struct *md, struct btrfs_root *root,
int i, ret = 0;
memset(md, 0, sizeof(*md));
- pthread_cond_init(&md->cond, NULL);
- pthread_mutex_init(&md->mutex, NULL);
+ md->cluster = calloc(1, BLOCK_SIZE);
+ if (!md->cluster)
+ return -ENOMEM;
+ md->threads = calloc(num_threads, sizeof(pthread_t));
+ if (!md->threads) {
+ free(md->cluster);
+ return -ENOMEM;
+ }
INIT_LIST_HEAD(&md->list);
INIT_LIST_HEAD(&md->ordered);
md->root = root;
md->out = out;
md->pending_start = (u64)-1;
md->compress_level = compress_level;
- md->cluster = calloc(1, BLOCK_SIZE);
md->sanitize_names = sanitize_names;
if (sanitize_names > 1)
crc32c_optimization_init();
- if (!md->cluster) {
- pthread_cond_destroy(&md->cond);
- pthread_mutex_destroy(&md->mutex);
- return -ENOMEM;
- }
-
+ md->name_tree.rb_node = NULL;
+ md->num_threads = num_threads;
+ pthread_cond_init(&md->cond, NULL);
+ pthread_mutex_init(&md->mutex, NULL);
meta_cluster_init(md, 0);
+
if (!num_threads)
return 0;
- md->name_tree.rb_node = NULL;
- md->num_threads = num_threads;
- md->threads = calloc(num_threads, sizeof(pthread_t));
- if (!md->threads) {
- free(md->cluster);
- pthread_cond_destroy(&md->cond);
- pthread_mutex_destroy(&md->mutex);
- return -ENOMEM;
- }
-
for (i = 0; i < num_threads; i++) {
ret = pthread_create(md->threads + i, NULL, dump_worker, md);
if (ret)
@@ -1470,8 +1464,7 @@ static int update_super(struct mdrestore_struct *mdres, u8 *buffer)
btrfs_set_stack_chunk_sub_stripes(chunk, 0);
btrfs_set_stack_chunk_type(chunk,
BTRFS_BLOCK_GROUP_SYSTEM);
- btrfs_set_stack_stripe_devid(&chunk->stripe,
- super->dev_item.devid);
+ chunk->stripe.devid = super->dev_item.devid;
physical = logical_to_physical(mdres, key.offset,
&size);
if (size != (u64)-1)
@@ -2829,9 +2822,8 @@ int main(int argc, char *argv[])
OPEN_CTREE_PARTIAL |
OPEN_CTREE_RESTORE);
if (!info) {
- int e = errno;
fprintf(stderr, "unable to open %s error = %s\n",
- target, strerror(e));
+ target, strerror(errno));
return 1;
}
diff --git a/btrfs-list.c b/btrfs-list.c
index 7529e11..2da54bf 100644
--- a/btrfs-list.c
+++ b/btrfs-list.c
@@ -630,7 +630,7 @@ static int resolve_root(struct root_lookup *rl, struct root_info *ri,
static int lookup_ino_path(int fd, struct root_info *ri)
{
struct btrfs_ioctl_ino_lookup_args args;
- int ret, e;
+ int ret;
if (ri->path)
return 0;
@@ -643,15 +643,14 @@ static int lookup_ino_path(int fd, struct root_info *ri)
args.objectid = ri->dir_id;
ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args);
- e = errno;
- if (ret) {
- if (e == ENOENT) {
+ if (ret < 0) {
+ if (errno == ENOENT) {
ri->ref_tree = 0;
return -ENOENT;
}
fprintf(stderr, "ERROR: Failed to lookup path for root %llu - %s\n",
(unsigned long long)ri->ref_tree,
- strerror(e));
+ strerror(errno));
return ret;
}
@@ -694,18 +693,16 @@ static u64 find_root_gen(int fd)
unsigned long off = 0;
u64 max_found = 0;
int i;
- int e;
memset(&ino_args, 0, sizeof(ino_args));
ino_args.objectid = BTRFS_FIRST_FREE_OBJECTID;
/* this ioctl fills in ino_args->treeid */
ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &ino_args);
- e = errno;
- if (ret) {
+ if (ret < 0) {
fprintf(stderr, "ERROR: Failed to lookup path for dirid %llu - %s\n",
(unsigned long long)BTRFS_FIRST_FREE_OBJECTID,
- strerror(e));
+ strerror(errno));
return 0;
}
@@ -728,10 +725,9 @@ static u64 find_root_gen(int fd)
while (1) {
ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
- e = errno;
if (ret < 0) {
fprintf(stderr, "ERROR: can't perform the search - %s\n",
- strerror(e));
+ strerror(errno));
return 0;
}
/* the ioctl returns the number of item it found in nr_items */
@@ -785,16 +781,14 @@ static char *__ino_resolve(int fd, u64 dirid)
struct btrfs_ioctl_ino_lookup_args args;
int ret;
char *full;
- int e;
memset(&args, 0, sizeof(args));
args.objectid = dirid;
ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args);
- e = errno;
- if (ret) {
+ if (ret < 0) {
fprintf(stderr, "ERROR: Failed to lookup path for dirid %llu - %s\n",
- (unsigned long long)dirid, strerror(e) );
+ (unsigned long long)dirid, strerror(errno));
return ERR_PTR(ret);
}
@@ -852,7 +846,6 @@ static char *ino_resolve(int fd, u64 ino, u64 *cache_dirid, char **cache_name)
struct btrfs_ioctl_search_header *sh;
unsigned long off = 0;
int namelen;
- int e;
memset(&args, 0, sizeof(args));
@@ -871,10 +864,9 @@ static char *ino_resolve(int fd, u64 ino, u64 *cache_dirid, char **cache_name)
sk->nr_items = 1;
ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
- e = errno;
if (ret < 0) {
fprintf(stderr, "ERROR: can't perform the search - %s\n",
- strerror(e));
+ strerror(errno));
return NULL;
}
/* the ioctl returns the number of item it found in nr_items */
@@ -1686,7 +1678,6 @@ int btrfs_list_find_updated_files(int fd, u64 root_id, u64 oldest_gen)
u64 found_gen;
u64 max_found = 0;
int i;
- int e;
u64 cache_dirid = 0;
u64 cache_ino = 0;
char *cache_dir_name = NULL;
@@ -1713,10 +1704,9 @@ int btrfs_list_find_updated_files(int fd, u64 root_id, u64 oldest_gen)
max_found = find_root_gen(fd);
while(1) {
ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
- e = errno;
if (ret < 0) {
fprintf(stderr, "ERROR: can't perform the search - %s\n",
- strerror(e));
+ strerror(errno));
break;
}
/* the ioctl returns the number of item it found in nr_items */
diff --git a/btrfs-show-super.c b/btrfs-show-super.c
index d8ad69e..051bd11 100644
--- a/btrfs-show-super.c
+++ b/btrfs-show-super.c
@@ -144,17 +144,15 @@ static int load_and_dump_sb(char *filename, int fd, u64 sb_bytenr, int full,
ret = pread64(fd, super_block_data, BTRFS_SUPER_INFO_SIZE, sb_bytenr);
if (ret != BTRFS_SUPER_INFO_SIZE) {
- int e = errno;
-
/* check if the disk if too short for further superblock */
- if (ret == 0 && e == 0)
+ if (ret == 0 && errno == 0)
return 0;
fprintf(stderr,
"ERROR: Failed to read the superblock on %s at %llu\n",
filename, (unsigned long long)sb_bytenr);
fprintf(stderr,
- "ERROR: error = '%s', errno = %d\n", strerror(e), e);
+ "ERROR: error = '%s', errno = %d\n", strerror(errno), errno);
return 1;
}
printf("superblock: bytenr=%llu, device=%s\n", sb_bytenr, filename);
@@ -186,11 +184,14 @@ static void print_sys_chunk_array(struct btrfs_super_block *sb)
struct extent_buffer *buf;
struct btrfs_disk_key *disk_key;
struct btrfs_chunk *chunk;
- struct btrfs_key key;
- u8 *ptr, *array_end;
+ u8 *array_ptr;
+ unsigned long sb_array_offset;
u32 num_stripes;
+ u32 array_size;
u32 len = 0;
- int i = 0;
+ u32 cur_offset;
+ struct btrfs_key key;
+ int item;
buf = malloc(sizeof(*buf) + sizeof(*sb));
if (!buf) {
@@ -198,34 +199,70 @@ static void print_sys_chunk_array(struct btrfs_super_block *sb)
exit(1);
}
write_extent_buffer(buf, sb, 0, sizeof(*sb));
- ptr = sb->sys_chunk_array;
- array_end = ptr + btrfs_super_sys_array_size(sb);
+ array_size = btrfs_super_sys_array_size(sb);
+
+ array_ptr = sb->sys_chunk_array;
+ sb_array_offset = offsetof(struct btrfs_super_block, sys_chunk_array);
+ cur_offset = 0;
+ item = 0;
+
+ while (cur_offset < array_size) {
+ disk_key = (struct btrfs_disk_key *)array_ptr;
+ len = sizeof(*disk_key);
+ if (cur_offset + len > array_size)
+ goto out_short_read;
- while (ptr < array_end) {
- disk_key = (struct btrfs_disk_key *)ptr;
btrfs_disk_key_to_cpu(&key, disk_key);
- printf("\titem %d ", i);
- btrfs_print_key(disk_key);
+ array_ptr += len;
+ sb_array_offset += len;
+ cur_offset += len;
- len = sizeof(*disk_key);
+ printf("\titem %d ", item);
+ btrfs_print_key(disk_key);
putchar('\n');
- ptr += len;
if (key.type == BTRFS_CHUNK_ITEM_KEY) {
- chunk = (struct btrfs_chunk *)(ptr - (u8 *)sb);
+ chunk = (struct btrfs_chunk *)sb_array_offset;
+ /*
+ * At least one btrfs_chunk with one stripe must be
+ * present, exact stripe count check comes afterwards
+ */
+ len = btrfs_chunk_item_size(1);
+ if (cur_offset + len > array_size)
+ goto out_short_read;
+
print_chunk(buf, chunk);
num_stripes = btrfs_chunk_num_stripes(buf, chunk);
+ if (!num_stripes) {
+ printk(
+ "ERROR: invalid number of stripes %u in sys_array at offset %u\n",
+ num_stripes, cur_offset);
+ break;
+ }
len = btrfs_chunk_item_size(num_stripes);
+ if (cur_offset + len > array_size)
+ goto out_short_read;
} else {
- BUG();
+ printk(
+ "ERROR: unexpected item type %u in sys_array at offset %u\n",
+ (u32)key.type, cur_offset);
+ break;
}
+ array_ptr += len;
+ sb_array_offset += len;
+ cur_offset += len;
- ptr += len;
- i++;
+ item++;
}
free(buf);
+ return;
+
+out_short_read:
+ printk("ERROR: sys_array too short to read %u bytes at offset %u\n",
+ len, cur_offset);
+ free(buf);
}
static int empty_backup(struct btrfs_root_backup *backup)
@@ -295,7 +332,7 @@ struct readable_flag_entry {
#define DEF_INCOMPAT_FLAG_ENTRY(bit_name) \
{BTRFS_FEATURE_INCOMPAT_##bit_name, #bit_name}
-struct readable_flag_entry incompat_flags_array[] = {
+static struct readable_flag_entry incompat_flags_array[] = {
DEF_INCOMPAT_FLAG_ENTRY(MIXED_BACKREF),
DEF_INCOMPAT_FLAG_ENTRY(DEFAULT_SUBVOL),
DEF_INCOMPAT_FLAG_ENTRY(MIXED_GROUPS),
@@ -315,7 +352,7 @@ static const int incompat_flags_num = sizeof(incompat_flags_array) /
#define DEF_SUPER_FLAG_ENTRY(bit_name) \
{BTRFS_SUPER_FLAG_##bit_name, #bit_name}
-struct readable_flag_entry super_flags_array[] = {
+static struct readable_flag_entry super_flags_array[] = {
DEF_HEADER_FLAG_ENTRY(WRITTEN),
DEF_HEADER_FLAG_ENTRY(RELOC),
DEF_SUPER_FLAG_ENTRY(CHANGING_FSID),
diff --git a/btrfs.c b/btrfs.c
index 14b556b..cc70515 100644
--- a/btrfs.c
+++ b/btrfs.c
@@ -37,15 +37,6 @@ static inline const char *skip_prefix(const char *str, const char *prefix)
return strncmp(str, prefix, len) ? NULL : str + len;
}
-int prefixcmp(const char *str, const char *prefix)
-{
- for (; ; str++, prefix++)
- if (!*prefix)
- return 0;
- else if (*str != *prefix)
- return (unsigned char)*prefix - (unsigned char)*str;
-}
-
static int parse_one_token(const char *arg, const struct cmd_group *grp,
const struct cmd_struct **cmd_ret)
{
diff --git a/btrfsck.h b/btrfsck.h
index 0882a38..e16f52f 100644
--- a/btrfsck.h
+++ b/btrfsck.h
@@ -142,6 +142,23 @@ static inline unsigned long btrfs_chunk_record_size(int num_stripes)
}
void free_chunk_cache_tree(struct cache_tree *chunk_cache);
+/*
+ * Function to check validation for num_stripes, or it can call
+ * float point error for 0 division
+ * return < 0 for invalid combination
+ * return 0 for valid combination
+ */
+static inline int check_num_stripes(u64 type, int num_stripes)
+{
+ if (num_stripes == 0)
+ return -1;
+ if (type & BTRFS_BLOCK_GROUP_RAID5 && num_stripes <= 1)
+ return -1;
+ if (type & BTRFS_BLOCK_GROUP_RAID6 && num_stripes <= 2)
+ return -1;
+ return 0;
+}
+
u64 calc_stripe_length(u64 type, u64 length, int num_stripes);
/* For block group tree */
static inline void block_group_tree_init(struct block_group_tree *tree)
diff --git a/chunk-recover.c b/chunk-recover.c
index f8693f7..b03330b 100644
--- a/chunk-recover.c
+++ b/chunk-recover.c
@@ -1159,9 +1159,9 @@ static int __rebuild_chunk_root(struct btrfs_trans_handle *trans,
if (min_devid > dev->devid)
min_devid = dev->devid;
}
- disk_key.objectid = BTRFS_DEV_ITEMS_OBJECTID;
- disk_key.type = BTRFS_DEV_ITEM_KEY;
- disk_key.offset = min_devid;
+ btrfs_set_disk_key_objectid(&disk_key, BTRFS_DEV_ITEMS_OBJECTID);
+ btrfs_set_disk_key_type(&disk_key, BTRFS_DEV_ITEM_KEY);
+ btrfs_set_disk_key_offset(&disk_key, min_devid);
cow = btrfs_alloc_free_block(trans, root, root->nodesize,
BTRFS_CHUNK_TREE_OBJECTID,
@@ -1191,13 +1191,10 @@ static int __rebuild_device_items(struct btrfs_trans_handle *trans,
{
struct btrfs_device *dev;
struct btrfs_key key;
- struct btrfs_dev_item *dev_item;
+ struct btrfs_dev_item dev_item_tmp;
+ struct btrfs_dev_item *dev_item = &dev_item_tmp;
int ret = 0;
- dev_item = malloc(sizeof(struct btrfs_dev_item));
- if (!dev_item)
- return -ENOMEM;
-
list_for_each_entry(dev, &rc->fs_devices->devices, dev_list) {
key.objectid = BTRFS_DEV_ITEMS_OBJECTID;
key.type = BTRFS_DEV_ITEM_KEY;
@@ -1218,7 +1215,6 @@ static int __rebuild_device_items(struct btrfs_trans_handle *trans,
dev_item, sizeof(*dev_item));
}
- free(dev_item);
return ret;
}
@@ -1238,7 +1234,7 @@ static int __insert_chunk_item(struct btrfs_trans_handle *trans,
key.offset = chunk_rec->offset;
ret = btrfs_insert_item(trans, chunk_root, &key, chunk,
- btrfs_chunk_item_size(chunk->num_stripes));
+ btrfs_chunk_item_size(chunk_rec->num_stripes));
free(chunk);
return ret;
}
@@ -1524,6 +1520,7 @@ static int recover_prepare(struct recover_control *rc, char *path)
int ret;
int fd;
struct btrfs_super_block *sb;
+ char buf[BTRFS_SUPER_INFO_SIZE];
struct btrfs_fs_devices *fs_devices;
ret = 0;
@@ -1533,17 +1530,11 @@ static int recover_prepare(struct recover_control *rc, char *path)
return -1;
}
- sb = malloc(BTRFS_SUPER_INFO_SIZE);
- if (!sb) {
- fprintf(stderr, "allocating memory for sb failed.\n");
- ret = -ENOMEM;
- goto fail_close_fd;
- }
-
+ sb = (struct btrfs_super_block*)buf;
ret = btrfs_read_dev_super(fd, sb, BTRFS_SUPER_INFO_OFFSET, 1);
if (ret) {
fprintf(stderr, "read super block error\n");
- goto fail_free_sb;
+ goto out_close_fd;
}
rc->sectorsize = btrfs_super_sectorsize(sb);
@@ -1556,21 +1547,19 @@ static int recover_prepare(struct recover_control *rc, char *path)
if (btrfs_super_flags(sb) & BTRFS_SUPER_FLAG_SEEDING) {
fprintf(stderr, "this device is seed device\n");
ret = -1;
- goto fail_free_sb;
+ goto out_close_fd;
}
ret = btrfs_scan_fs_devices(fd, path, &fs_devices, 0, 1, 0);
if (ret)
- goto fail_free_sb;
+ goto out_close_fd;
rc->fs_devices = fs_devices;
if (rc->verbose)
print_all_devices(&rc->fs_devices->devices);
-fail_free_sb:
- free(sb);
-fail_close_fd:
+out_close_fd:
close(fd);
return ret;
}
@@ -1618,16 +1607,19 @@ static int btrfs_verify_device_extents(struct block_group_record *bg,
struct list_head *devexts, int ndevexts)
{
struct device_extent_record *devext;
- u64 strpie_length;
+ u64 stripe_length;
int expected_num_stripes;
expected_num_stripes = calc_num_stripes(bg->flags);
if (expected_num_stripes && expected_num_stripes != ndevexts)
return 1;
- strpie_length = calc_stripe_length(bg->flags, bg->offset, ndevexts);
+ if (check_num_stripes(bg->flags, ndevexts) < 0)
+ return 1;
+
+ stripe_length = calc_stripe_length(bg->flags, bg->offset, ndevexts);
list_for_each_entry(devext, devexts, chunk_list) {
- if (devext->length != strpie_length)
+ if (devext->length != stripe_length)
return 1;
}
return 0;
diff --git a/cmds-balance.c b/cmds-balance.c
index c5be6b9..e0d10aa 100644
--- a/cmds-balance.c
+++ b/cmds-balance.c
@@ -53,7 +53,7 @@ static int parse_one_profile(const char *profile, u64 *flags)
} else if (!strcmp(profile, "single")) {
*flags |= BTRFS_AVAIL_ALLOC_BIT_SINGLE;
} else {
- fprintf(stderr, "Unknown profile '%s'\n", profile);
+ error("unknown profile: %s", profile);
return 1;
}
@@ -123,14 +123,13 @@ static int parse_range(const char *range, u64 *start, u64 *end)
*start = 0;
skipped++;
} else {
- *end = strtoull(range, &endptr, 10);
+ *start = strtoull(range, &endptr, 10);
if (*endptr != 0 && *endptr != '.')
return 1;
}
if (*start > *end) {
- fprintf(stderr,
- "ERROR: range %llu..%llu doesn't make sense\n",
+ error("range %llu..%llu doesn't make sense",
(unsigned long long)*start,
(unsigned long long)*end);
return 1;
@@ -149,8 +148,7 @@ static int parse_range_strict(const char *range, u64 *start, u64 *end)
{
if (parse_range(range, start, end) == 0) {
if (*start >= *end) {
- fprintf(stderr,
- "ERROR: range %llu..%llu not allowed\n",
+ error("range %llu..%llu not allowed",
(unsigned long long)*start,
(unsigned long long)*end);
return 1;
@@ -229,71 +227,79 @@ static int parse_filters(char *filters, struct btrfs_balance_args *args)
*value++ = 0;
if (!strcmp(this_char, "profiles")) {
if (!value || !*value) {
- fprintf(stderr, "the profiles filter requires "
- "an argument\n");
+ error("the profiles filter requires an argument");
return 1;
}
if (parse_profiles(value, &args->profiles)) {
- fprintf(stderr, "Invalid profiles argument\n");
+ error("invalid profiles argument");
return 1;
}
args->flags |= BTRFS_BALANCE_ARGS_PROFILES;
} else if (!strcmp(this_char, "usage")) {
if (!value || !*value) {
- fprintf(stderr, "the usage filter requires "
- "an argument\n");
+ error("the usage filter requires an argument");
return 1;
}
- if (parse_u64(value, &args->usage) ||
- args->usage > 100) {
- fprintf(stderr, "Invalid usage argument: %s\n",
- value);
- return 1;
+ if (parse_u64(value, &args->usage)) {
+ if (parse_range_u32(value, &args->usage_min,
+ &args->usage_max)) {
+ error("invalid usage argument: %s",
+ value);
+ return 1;
+ }
+ if (args->usage_max > 100) {
+ error("invalid usage argument: %s",
+ value);
+ }
+ args->flags &= ~BTRFS_BALANCE_ARGS_USAGE;
+ args->flags |= BTRFS_BALANCE_ARGS_USAGE_RANGE;
+ } else {
+ if (args->usage > 100) {
+ error("invalid usage argument: %s",
+ value);
+ return 1;
+ }
+ args->flags &= ~BTRFS_BALANCE_ARGS_USAGE_RANGE;
+ args->flags |= BTRFS_BALANCE_ARGS_USAGE;
}
args->flags |= BTRFS_BALANCE_ARGS_USAGE;
} else if (!strcmp(this_char, "devid")) {
if (!value || !*value) {
- fprintf(stderr, "the devid filter requires "
- "an argument\n");
+ error("the devid filter requires an argument");
return 1;
}
- if (parse_u64(value, &args->devid) ||
- args->devid == 0) {
- fprintf(stderr, "Invalid devid argument: %s\n",
- value);
+ if (parse_u64(value, &args->devid) || args->devid == 0) {
+ error("invalid devid argument: %s", value);
return 1;
}
args->flags |= BTRFS_BALANCE_ARGS_DEVID;
} else if (!strcmp(this_char, "drange")) {
if (!value || !*value) {
- fprintf(stderr, "the drange filter requires "
- "an argument\n");
+ error("the drange filter requires an argument");
return 1;
}
if (parse_range_strict(value, &args->pstart, &args->pend)) {
- fprintf(stderr, "Invalid drange argument\n");
+ error("invalid drange argument");
return 1;
}
args->flags |= BTRFS_BALANCE_ARGS_DRANGE;
} else if (!strcmp(this_char, "vrange")) {
if (!value || !*value) {
- fprintf(stderr, "the vrange filter requires "
- "an argument\n");
+ error("the vrange filter requires an argument");
return 1;
}
if (parse_range_strict(value, &args->vstart, &args->vend)) {
- fprintf(stderr, "Invalid vrange argument\n");
+ error("invalid vrange argument");
return 1;
}
args->flags |= BTRFS_BALANCE_ARGS_VRANGE;
} else if (!strcmp(this_char, "convert")) {
if (!value || !*value) {
- fprintf(stderr, "the convert option requires "
- "an argument\n");
+ error("the convert option requires an argument");
return 1;
}
if (parse_one_profile(value, &args->target)) {
- fprintf(stderr, "Invalid convert argument\n");
+ error("invalid convert argument");
return 1;
}
args->flags |= BTRFS_BALANCE_ARGS_CONVERT;
@@ -301,19 +307,35 @@ static int parse_filters(char *filters, struct btrfs_balance_args *args)
args->flags |= BTRFS_BALANCE_ARGS_SOFT;
} else if (!strcmp(this_char, "limit")) {
if (!value || !*value) {
- fprintf(stderr,
- "the limit filter requires an argument\n");
+ error("the limit filter requires an argument");
return 1;
}
if (parse_u64(value, &args->limit)) {
- fprintf(stderr, "Invalid limit argument: %s\n",
- value);
+ if (parse_range_u32(value, &args->limit_min,
+ &args->limit_max)) {
+ error("Invalid limit argument: %s",
+ value);
+ return 1;
+ }
+ args->flags &= ~BTRFS_BALANCE_ARGS_LIMIT;
+ args->flags |= BTRFS_BALANCE_ARGS_LIMIT_RANGE;
+ } else {
+ args->flags &= ~BTRFS_BALANCE_ARGS_LIMIT_RANGE;
+ args->flags |= BTRFS_BALANCE_ARGS_LIMIT;
+ }
+ } else if (!strcmp(this_char, "stripes")) {
+ if (!value || !*value) {
+ error("the stripes filter requires an argument");
+ return 1;
+ }
+ if (parse_range_u32(value, &args->stripes_min,
+ &args->stripes_max)) {
+ error("invalid stripes argument");
return 1;
}
- args->flags |= BTRFS_BALANCE_ARGS_LIMIT;
+ args->flags |= BTRFS_BALANCE_ARGS_STRIPES_RANGE;
} else {
- fprintf(stderr, "Unrecognized balance option '%s'\n",
- this_char);
+ error("unrecognized balance option: %s", this_char);
return 1;
}
}
@@ -335,6 +357,10 @@ static void dump_balance_args(struct btrfs_balance_args *args)
printf(", profiles=%llu", (unsigned long long)args->profiles);
if (args->flags & BTRFS_BALANCE_ARGS_USAGE)
printf(", usage=%llu", (unsigned long long)args->usage);
+ if (args->flags & BTRFS_BALANCE_ARGS_USAGE_RANGE) {
+ printf(", usage=");
+ print_range_u32(args->usage_min, args->usage_max);
+ }
if (args->flags & BTRFS_BALANCE_ARGS_DEVID)
printf(", devid=%llu", (unsigned long long)args->devid);
if (args->flags & BTRFS_BALANCE_ARGS_DRANGE)
@@ -347,6 +373,14 @@ static void dump_balance_args(struct btrfs_balance_args *args)
(unsigned long long)args->vend);
if (args->flags & BTRFS_BALANCE_ARGS_LIMIT)
printf(", limit=%llu", (unsigned long long)args->limit);
+ if (args->flags & BTRFS_BALANCE_ARGS_LIMIT_RANGE) {
+ printf(", limit=");
+ print_range_u32(args->limit_min, args->limit_max);
+ }
+ if (args->flags & BTRFS_BALANCE_ARGS_STRIPES_RANGE) {
+ printf(", stripes=");
+ print_range_u32(args->stripes_min, args->stripes_max);
+ }
printf("\n");
}
@@ -418,11 +452,10 @@ static int do_balance(const char *path, struct btrfs_ioctl_balance_args *args,
fprintf(stderr, "balance canceled by user\n");
ret = 0;
} else {
- fprintf(stderr, "ERROR: error during balancing '%s' "
- "- %s\n", path, strerror(e));
+ error("error during balancing '%s': %s", path, strerror(e));
if (e != EINPROGRESS)
- fprintf(stderr, "There may be more info in "
- "syslog - try dmesg | tail\n");
+ fprintf(stderr,
+ "There may be more info in syslog - try dmesg | tail\n");
ret = 1;
}
} else {
@@ -523,9 +556,9 @@ static int cmd_balance_start(int argc, char **argv)
*/
if (args.flags & BTRFS_BALANCE_SYSTEM) {
if (!force) {
- fprintf(stderr,
-"Refusing to explicitly operate on system chunks.\n"
-"Pass --force if you really want to do that.\n");
+ error(
+ "Refusing to explicitly operate on system chunks.\n"
+ "Pass --force if you really want to do that.");
return 1;
}
} else if (args.flags & BTRFS_BALANCE_METADATA) {
@@ -543,8 +576,7 @@ static int cmd_balance_start(int argc, char **argv)
for (i = 0; ptrs[i]; i++) {
if ((ptrs[i]->flags & BTRFS_BALANCE_ARGS_DRANGE) &&
!(ptrs[i]->flags & BTRFS_BALANCE_ARGS_DEVID)) {
- fprintf(stderr, "drange filter can be used only if "
- "devid filter is used\n");
+ error("drange filter must be used with devid filter");
return 1;
}
}
@@ -553,8 +585,7 @@ static int cmd_balance_start(int argc, char **argv)
for (i = 0; ptrs[i]; i++) {
if ((ptrs[i]->flags & BTRFS_BALANCE_ARGS_SOFT) &&
!(ptrs[i]->flags & BTRFS_BALANCE_ARGS_CONVERT)) {
- fprintf(stderr, "'soft' option can be used only if "
- "changing profiles\n");
+ error("'soft' option can be used only when converting profiles");
return 1;
}
}
@@ -595,8 +626,8 @@ static int cmd_balance_pause(int argc, char **argv)
close_file_or_dir(fd, dirstream);
if (ret < 0) {
- fprintf(stderr, "ERROR: balance pause on '%s' failed - %s\n",
- path, (e == ENOTCONN) ? "Not running" : strerror(e));
+ error("balance pause on '%s' failed: %s", path,
+ (e == ENOTCONN) ? "Not running" : strerror(e));
if (e == ENOTCONN)
return 2;
else
@@ -634,8 +665,8 @@ static int cmd_balance_cancel(int argc, char **argv)
close_file_or_dir(fd, dirstream);
if (ret < 0) {
- fprintf(stderr, "ERROR: balance cancel on '%s' failed - %s\n",
- path, (e == ENOTCONN) ? "Not in progress" : strerror(e));
+ error("balance cancel on '%s' failed: %s", path,
+ (e == ENOTCONN) ? "Not in progress" : strerror(e));
if (e == ENOTCONN)
return 2;
else
@@ -683,8 +714,7 @@ static int cmd_balance_resume(int argc, char **argv)
if (args.state & BTRFS_BALANCE_STATE_CANCEL_REQ)
fprintf(stderr, "balance canceled by user\n");
} else if (e == ENOTCONN || e == EINPROGRESS) {
- fprintf(stderr, "ERROR: balance resume on '%s' "
- "failed - %s\n", path,
+ error("balance resume on '%s' failed: %s", path,
(e == ENOTCONN) ? "Not in progress" :
"Already running");
if (e == ENOTCONN)
@@ -692,9 +722,9 @@ static int cmd_balance_resume(int argc, char **argv)
else
return 1;
} else {
- fprintf(stderr,
-"ERROR: error during balancing '%s' - %s\n"
-"There may be more info in syslog - try dmesg | tail\n", path, strerror(e));
+ error("error during balancing '%s': %s\n"
+ "There may be more info in syslog - try dmesg | tail",
+ path, strerror(e));
return 1;
}
} else {
@@ -769,8 +799,7 @@ static int cmd_balance_status(int argc, char **argv)
printf("No balance found on '%s'\n", path);
return 0;
}
- fprintf(stderr, "ERROR: balance status on '%s' failed - %s\n",
- path, strerror(e));
+ error("balance status on '%s' failed: %s", path, strerror(e));
return 2;
}
diff --git a/cmds-check.c b/cmds-check.c
index fd661d9..0165fba 100644
--- a/cmds-check.c
+++ b/cmds-check.c
@@ -35,6 +35,7 @@
#include "utils.h"
#include "commands.h"
#include "free-space-cache.h"
+#include "free-space-tree.h"
#include "btrfsck.h"
#include "qgroup-verify.h"
#include "rbtree-utils.h"
@@ -566,12 +567,15 @@ static struct inode_record *clone_inode_rec(struct inode_record *orig_rec)
struct inode_record *rec;
struct inode_backref *backref;
struct inode_backref *orig;
+ struct inode_backref *tmp;
struct orphan_data_extent *src_orphan;
struct orphan_data_extent *dst_orphan;
size_t size;
int ret;
rec = malloc(sizeof(*rec));
+ if (!rec)
+ return ERR_PTR(-ENOMEM);
memcpy(rec, orig_rec, sizeof(*rec));
rec->refs = 1;
INIT_LIST_HEAD(&rec->backrefs);
@@ -581,13 +585,19 @@ static struct inode_record *clone_inode_rec(struct inode_record *orig_rec)
list_for_each_entry(orig, &orig_rec->backrefs, list) {
size = sizeof(*orig) + orig->namelen + 1;
backref = malloc(size);
+ if (!backref) {
+ ret = -ENOMEM;
+ goto cleanup;
+ }
memcpy(backref, orig, size);
list_add_tail(&backref->list, &rec->backrefs);
}
list_for_each_entry(src_orphan, &orig_rec->orphan_extents, list) {
dst_orphan = malloc(sizeof(*dst_orphan));
- /* TODO: Fix all the HELL of un-catched -ENOMEM case */
- BUG_ON(!dst_orphan);
+ if (!dst_orphan) {
+ ret = -ENOMEM;
+ goto cleanup;
+ }
memcpy(dst_orphan, src_orphan, sizeof(*src_orphan));
list_add_tail(&dst_orphan->list, &rec->orphan_extents);
}
@@ -595,6 +605,23 @@ static struct inode_record *clone_inode_rec(struct inode_record *orig_rec)
BUG_ON(ret < 0);
return rec;
+
+cleanup:
+ if (!list_empty(&rec->backrefs))
+ list_for_each_entry_safe(orig, tmp, &rec->backrefs, list) {
+ list_del(&orig->list);
+ free(orig);
+ }
+
+ if (!list_empty(&rec->orphan_extents))
+ list_for_each_entry_safe(orig, tmp, &rec->orphan_extents, list) {
+ list_del(&orig->list);
+ free(orig);
+ }
+
+ free(rec);
+
+ return ERR_PTR(ret);
}
static void print_orphan_data_extents(struct list_head *orphan_extents,
@@ -730,11 +757,15 @@ static struct inode_record *get_inode_rec(struct cache_tree *inode_cache,
rec = node->data;
if (mod && rec->refs > 1) {
node->data = clone_inode_rec(rec);
+ if (IS_ERR(node->data))
+ return node->data;
rec->refs--;
rec = node->data;
}
} else if (mod) {
rec = calloc(1, sizeof(*rec));
+ if (!rec)
+ return ERR_PTR(-ENOMEM);
rec->ino = ino;
rec->extent_start = (u64)-1;
rec->refs = 1;
@@ -743,6 +774,10 @@ static struct inode_record *get_inode_rec(struct cache_tree *inode_cache,
rec->holes = RB_ROOT;
node = malloc(sizeof(*node));
+ if (!node) {
+ free(rec);
+ return ERR_PTR(-ENOMEM);
+ }
node->cache.start = ino;
node->cache.size = 1;
node->data = rec;
@@ -751,7 +786,8 @@ static struct inode_record *get_inode_rec(struct cache_tree *inode_cache,
rec->found_link = 1;
ret = insert_cache_extent(inode_cache, &node->cache);
- BUG_ON(ret);
+ if (ret)
+ return ERR_PTR(-EEXIST);
}
return rec;
}
@@ -810,7 +846,8 @@ static void maybe_free_inode_rec(struct cache_tree *inode_cache,
if (backref->found_dir_item && backref->found_dir_index) {
if (backref->filetype != filetype)
backref->errors |= REF_ERR_FILETYPE_UNMATCH;
- if (!backref->errors && backref->found_inode_ref) {
+ if (!backref->errors && backref->found_inode_ref &&
+ rec->nlink == rec->found_link) {
list_del(&backref->list);
free(backref);
}
@@ -916,6 +953,8 @@ static struct inode_backref *get_inode_backref(struct inode_record *rec,
}
backref = malloc(sizeof(*backref) + namelen + 1);
+ if (!backref)
+ return NULL;
memset(backref, 0, sizeof(*backref));
backref->dir = dir;
backref->namelen = namelen;
@@ -934,7 +973,9 @@ static int add_inode_backref(struct cache_tree *inode_cache,
struct inode_backref *backref;
rec = get_inode_rec(inode_cache, ino, 1);
+ BUG_ON(IS_ERR(rec));
backref = get_inode_backref(rec, name, namelen, dir);
+ BUG_ON(!backref);
if (errors)
backref->errors |= errors;
if (itemtype == BTRFS_DIR_INDEX_KEY) {
@@ -1088,6 +1129,7 @@ again:
ins = node;
} else {
ins = malloc(sizeof(*ins));
+ BUG_ON(!ins);
ins->cache.start = node->cache.start;
ins->cache.size = node->cache.size;
ins->data = rec;
@@ -1096,6 +1138,7 @@ again:
ret = insert_cache_extent(dst, &ins->cache);
if (ret == -EEXIST) {
conflict = get_inode_rec(dst, rec->ino, 1);
+ BUG_ON(IS_ERR(conflict));
merge_inode_recs(rec, conflict, dst);
if (rec->checked) {
conflict->checked = 1;
@@ -1123,6 +1166,7 @@ again:
maybe_free_inode_rec(dst, dst_node->current);
}
dst_node->current = get_inode_rec(dst, current_ino, 1);
+ BUG_ON(IS_ERR(dst_node->current));
}
return 0;
}
@@ -1160,6 +1204,8 @@ static int add_shared_node(struct cache_tree *shared, u64 bytenr, u32 refs)
struct shared_node *node;
node = calloc(1, sizeof(*node));
+ if (!node)
+ return -ENOMEM;
node->cache.start = bytenr;
node->cache.size = 1;
cache_tree_init(&node->root_cache);
@@ -1167,8 +1213,8 @@ static int add_shared_node(struct cache_tree *shared, u64 bytenr, u32 refs)
node->refs = refs;
ret = insert_cache_extent(shared, &node->cache);
- BUG_ON(ret);
- return 0;
+
+ return ret;
}
static int enter_shared_node(struct btrfs_root *root, u64 bytenr, u32 refs,
@@ -1176,6 +1222,7 @@ static int enter_shared_node(struct btrfs_root *root, u64 bytenr, u32 refs,
{
struct shared_node *node;
struct shared_node *dest;
+ int ret;
if (level == wc->active_node)
return 0;
@@ -1183,7 +1230,8 @@ static int enter_shared_node(struct btrfs_root *root, u64 bytenr, u32 refs,
BUG_ON(wc->active_node <= level);
node = find_shared_node(&wc->shared, bytenr);
if (!node) {
- add_shared_node(&wc->shared, bytenr, refs);
+ ret = add_shared_node(&wc->shared, bytenr, refs);
+ BUG_ON(ret);
node = find_shared_node(&wc->shared, bytenr);
wc->nodes[level] = node;
wc->active_node = level;
@@ -1663,6 +1711,7 @@ static int process_one_leaf(struct btrfs_root *root, struct extent_buffer *eb,
}
active_node->current = get_inode_rec(inode_cache,
key.objectid, 1);
+ BUG_ON(IS_ERR(active_node->current));
}
switch (key.type) {
case BTRFS_DIR_ITEM_KEY:
@@ -2063,6 +2112,7 @@ static int add_missing_dir_index(struct btrfs_root *root,
backref->found_dir_index = 1;
dir_rec = get_inode_rec(inode_cache, backref->dir, 0);
+ BUG_ON(IS_ERR(dir_rec));
if (!dir_rec)
return 0;
dir_rec->found_size += backref->namelen;
@@ -2392,7 +2442,7 @@ static int reset_nlink(struct btrfs_trans_handle *trans,
list_for_each_entry(backref, &rec->backrefs, list) {
ret = btrfs_add_link(trans, root, rec->ino, backref->dir,
backref->name, backref->namelen,
- backref->ref_type, &backref->index, 1);
+ backref->filetype, &backref->index, 1);
if (ret < 0)
goto out;
}
@@ -2887,6 +2937,7 @@ static int check_inode_recs(struct btrfs_root *root,
return err;
rec = get_inode_rec(inode_cache, root_dirid, 0);
+ BUG_ON(IS_ERR(rec));
if (rec) {
ret = check_root_dir(rec);
if (ret) {
@@ -2994,13 +3045,16 @@ static struct root_record *get_root_rec(struct cache_tree *root_cache,
rec = container_of(cache, struct root_record, cache);
} else {
rec = calloc(1, sizeof(*rec));
+ if (!rec)
+ return ERR_PTR(-ENOMEM);
rec->objectid = objectid;
INIT_LIST_HEAD(&rec->backrefs);
rec->cache.start = objectid;
rec->cache.size = 1;
ret = insert_cache_extent(root_cache, &rec->cache);
- BUG_ON(ret);
+ if (ret)
+ return ERR_PTR(-EEXIST);
}
return rec;
}
@@ -3021,6 +3075,8 @@ static struct root_backref *get_root_backref(struct root_record *rec,
}
backref = calloc(1, sizeof(*backref) + namelen + 1);
+ if (!backref)
+ return NULL;
backref->ref_root = ref_root;
backref->dir = dir;
backref->index = index;
@@ -3058,7 +3114,9 @@ static int add_root_backref(struct cache_tree *root_cache,
struct root_backref *backref;
rec = get_root_rec(root_cache, root_id);
+ BUG_ON(IS_ERR(rec));
backref = get_root_backref(rec, ref_root, dir, index, name, namelen);
+ BUG_ON(!backref);
backref->errors |= errors;
@@ -3163,6 +3221,7 @@ static int check_root_refs(struct btrfs_root *root,
int errors = 0;
rec = get_root_rec(root_cache, BTRFS_FS_TREE_OBJECTID);
+ BUG_ON(IS_ERR(rec));
rec->found_ref = 1;
/* fixme: this can not detect circular references */
@@ -3184,6 +3243,7 @@ static int check_root_refs(struct btrfs_root *root,
ref_root = get_root_rec(root_cache,
backref->ref_root);
+ BUG_ON(IS_ERR(ref_root));
if (ref_root->found_ref > 0)
continue;
@@ -3430,6 +3490,7 @@ static int check_fs_root(struct btrfs_root *root,
if (root->root_key.objectid != BTRFS_TREE_RELOC_OBJECTID) {
rec = get_root_rec(root_cache, root->root_key.objectid);
+ BUG_ON(IS_ERR(rec));
if (btrfs_root_refs(root_item) > 0)
rec->found_root_item = 1;
}
@@ -3446,6 +3507,7 @@ static int check_fs_root(struct btrfs_root *root,
inode = get_inode_rec(&root_node.inode_cache, orphan->objectid,
1);
+ BUG_ON(IS_ERR(inode));
inode->errors |= I_ERR_FILE_EXTENT_ORPHAN;
list_move(&orphan->list, &inode->orphan_extents);
}
@@ -4293,6 +4355,9 @@ static struct tree_backref *alloc_tree_backref(struct extent_record *rec,
u64 parent, u64 root)
{
struct tree_backref *ref = malloc(sizeof(*ref));
+
+ if (!ref)
+ return NULL;
memset(&ref->node, 0, sizeof(ref->node));
if (parent > 0) {
ref->parent = parent;
@@ -4349,6 +4414,9 @@ static struct data_backref *alloc_data_backref(struct extent_record *rec,
u64 max_size)
{
struct data_backref *ref = malloc(sizeof(*ref));
+
+ if (!ref)
+ return NULL;
memset(&ref->node, 0, sizeof(ref->node));
ref->node.is_data = 1;
@@ -4519,6 +4587,8 @@ static int add_extent_rec(struct cache_tree *extent_cache,
return ret;
}
rec = malloc(sizeof(*rec));
+ if (!rec)
+ return -ENOMEM;
rec->start = start;
rec->max_size = max_size;
rec->nr = max(nr, max_size);
@@ -4599,8 +4669,10 @@ static int add_tree_backref(struct cache_tree *extent_cache, u64 bytenr,
}
back = find_tree_backref(rec, parent, root);
- if (!back)
+ if (!back) {
back = alloc_tree_backref(rec, parent, root);
+ BUG_ON(!back);
+ }
if (found_ref) {
if (back->node.found_ref) {
@@ -4659,9 +4731,11 @@ static int add_data_backref(struct cache_tree *extent_cache, u64 bytenr,
*/
back = find_data_backref(rec, parent, root, owner, offset, found_ref,
bytenr, max_size);
- if (!back)
+ if (!back) {
back = alloc_data_backref(rec, parent, root, owner, offset,
max_size);
+ BUG_ON(!back);
+ }
if (found_ref) {
BUG_ON(num_refs != 1);
@@ -5134,6 +5208,10 @@ static int process_extent_item(struct btrfs_root *root,
ei = btrfs_item_ptr(eb, slot, struct btrfs_extent_item);
refs = btrfs_extent_refs(eb, ei);
+ if (btrfs_extent_flags(eb, ei) & BTRFS_EXTENT_FLAG_TREE_BLOCK)
+ metadata = 1;
+ else
+ metadata = 0;
add_extent_rec(extent_cache, NULL, 0, key.objectid, num_bytes,
refs, 0, 0, 0, metadata, 1, num_bytes);
@@ -5399,9 +5477,29 @@ static int check_space_cache(struct btrfs_root *root)
btrfs_remove_free_space_cache(cache);
}
- ret = load_free_space_cache(root->fs_info, cache);
- if (!ret)
- continue;
+ if (btrfs_fs_compat_ro(root->fs_info,
+ BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE)) {
+ ret = exclude_super_stripes(root, cache);
+ if (ret) {
+ fprintf(stderr, "could not exclude super stripes: %s\n",
+ strerror(-ret));
+ error++;
+ continue;
+ }
+ ret = load_free_space_tree(root->fs_info, cache);
+ free_excluded_extents(root, cache);
+ if (ret < 0) {
+ fprintf(stderr, "could not load free space tree: %s\n",
+ strerror(-ret));
+ error++;
+ continue;
+ }
+ error += ret;
+ } else {
+ ret = load_free_space_cache(root->fs_info, cache);
+ if (!ret)
+ continue;
+ }
ret = verify_space_cache(root, cache);
if (ret) {
@@ -9120,11 +9218,11 @@ static int build_roots_info_cache(struct btrfs_fs_info *info)
iref = (struct btrfs_extent_inline_ref *)(ei + 1);
level = found_key.offset;
} else {
- struct btrfs_tree_block_info *info;
+ struct btrfs_tree_block_info *binfo;
- info = (struct btrfs_tree_block_info *)(ei + 1);
- iref = (struct btrfs_extent_inline_ref *)(info + 1);
- level = btrfs_tree_block_level(leaf, info);
+ binfo = (struct btrfs_tree_block_info *)(ei + 1);
+ iref = (struct btrfs_extent_inline_ref *)(binfo + 1);
+ level = btrfs_tree_block_level(leaf, binfo);
}
/*
@@ -9640,8 +9738,12 @@ int cmd_check(int argc, char **argv)
goto close_out;
}
- if (!ctx.progress_enabled)
- fprintf(stderr, "checking free space cache\n");
+ if (!ctx.progress_enabled) {
+ if (btrfs_fs_compat_ro(info, BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE))
+ fprintf(stderr, "checking free space tree\n");
+ else
+ fprintf(stderr, "checking free space cache\n");
+ }
ret = check_space_cache(root);
if (ret)
goto out;
@@ -9730,7 +9832,6 @@ out:
printf("file data blocks allocated: %llu\n referenced %llu\n",
(unsigned long long)data_bytes_allocated,
(unsigned long long)data_bytes_referenced);
- printf("%s\n", PACKAGE_STRING);
free_root_recs_tree(&root_cache);
close_out:
diff --git a/cmds-device.c b/cmds-device.c
index c2f3a40..50c1c5d 100644
--- a/cmds-device.c
+++ b/cmds-device.c
@@ -49,7 +49,7 @@ static const char * const cmd_device_add_usage[] = {
static int cmd_device_add(int argc, char **argv)
{
char *mntpnt;
- int i, fdmnt, ret=0, e;
+ int i, fdmnt, ret = 0;
DIR *dirstream = NULL;
int discard = 1;
int force = 0;
@@ -102,7 +102,7 @@ static int cmd_device_add(int argc, char **argv)
devfd = open(argv[i], O_RDWR);
if (devfd < 0) {
- fprintf(stderr, "ERROR: Unable to open device '%s'\n", argv[i]);
+ error("unable to open device '%s'", argv[i]);
ret++;
continue;
}
@@ -117,8 +117,7 @@ static int cmd_device_add(int argc, char **argv)
path = canonicalize_path(argv[i]);
if (!path) {
- fprintf(stderr,
- "ERROR: Could not canonicalize pathname '%s': %s\n",
+ error("could not canonicalize pathname '%s': %s",
argv[i], strerror(errno));
ret++;
goto error_out;
@@ -127,10 +126,9 @@ static int cmd_device_add(int argc, char **argv)
memset(&ioctl_args, 0, sizeof(ioctl_args));
strncpy_null(ioctl_args.name, path);
res = ioctl(fdmnt, BTRFS_IOC_ADD_DEV, &ioctl_args);
- e = errno;
if (res < 0) {
- fprintf(stderr, "ERROR: error adding the device '%s' - %s\n",
- path, strerror(e));
+ error("error adding device '%s': %s",
+ path, strerror(errno));
ret++;
}
free(path);
@@ -145,7 +143,7 @@ static int _cmd_device_remove(int argc, char **argv,
const char * const *usagestr)
{
char *mntpnt;
- int i, fdmnt, ret=0, e;
+ int i, fdmnt, ret = 0;
DIR *dirstream = NULL;
if (check_argc_min(argc, 3))
@@ -161,25 +159,26 @@ static int _cmd_device_remove(int argc, char **argv,
struct btrfs_ioctl_vol_args arg;
int res;
- if (is_block_device(argv[i]) != 1) {
- fprintf(stderr,
- "ERROR: %s is not a block device\n", argv[i]);
+ if (is_block_device(argv[i]) != 1 && strcmp(argv[i], "missing")) {
+ error("not a block device: %s", argv[i]);
ret++;
continue;
}
memset(&arg, 0, sizeof(arg));
strncpy_null(arg.name, argv[i]);
+ /*
+ * Positive values are from BTRFS_ERROR_DEV_*,
+ * otherwise it's a generic error, one of errnos
+ */
res = ioctl(fdmnt, BTRFS_IOC_RM_DEV, &arg);
- e = errno;
if (res) {
const char *msg;
if (res > 0)
msg = btrfs_err_str(res);
else
- msg = strerror(e);
- fprintf(stderr,
- "ERROR: error removing the device '%s' - %s\n",
+ msg = strerror(errno);
+ error("error removing device '%s': %s",
argv[i], msg);
ret++;
}
@@ -251,11 +250,9 @@ static int cmd_device_scan(int argc, char **argv)
if (all || argc == 1) {
printf("Scanning for Btrfs filesystems\n");
ret = btrfs_scan_lblkid();
- if (ret)
- fprintf(stderr, "ERROR: error %d while scanning\n", ret);
+ error_on(ret, "error %d while scanning", ret);
ret = btrfs_register_all_devices();
- if (ret)
- fprintf(stderr, "ERROR: error %d while registering\n", ret);
+ error_on(ret, "error %d while registering devices", ret);
goto out;
}
@@ -263,15 +260,13 @@ static int cmd_device_scan(int argc, char **argv)
char *path;
if (is_block_device(argv[i]) != 1) {
- fprintf(stderr,
- "ERROR: %s is not a block device\n", argv[i]);
+ error("not a block device: %s", argv[i]);
ret = 1;
goto out;
}
path = canonicalize_path(argv[i]);
if (!path) {
- fprintf(stderr,
- "ERROR: Could not canonicalize path '%s': %s\n",
+ error("could not canonicalize path '%s': %s",
argv[i], strerror(errno));
ret = 1;
goto out;
@@ -313,16 +308,14 @@ static int cmd_device_ready(int argc, char **argv)
path = canonicalize_path(argv[argc - 1]);
if (!path) {
- fprintf(stderr,
- "ERROR: Could not canonicalize pathname '%s': %s\n",
+ error("could not canonicalize pathname '%s': %s",
argv[argc - 1], strerror(errno));
ret = 1;
goto out;
}
if (is_block_device(path) != 1) {
- fprintf(stderr,
- "ERROR: %s is not a block device\n", path);
+ error("not a block device: %s", path);
ret = 1;
goto out;
}
@@ -331,9 +324,8 @@ static int cmd_device_ready(int argc, char **argv)
strncpy_null(args.name, path);
ret = ioctl(fd, BTRFS_IOC_DEVICES_READY, &args);
if (ret < 0) {
- fprintf(stderr, "ERROR: unable to determine if the device '%s'"
- " is ready for mounting - %s\n", path,
- strerror(errno));
+ error("unable to determine if device '%s' is ready for mount: %s",
+ path, strerror(errno));
ret = 1;
}
@@ -388,13 +380,13 @@ static int cmd_device_stats(int argc, char **argv)
ret = get_fs_info(dev_path, &fi_args, &di_args);
if (ret) {
- fprintf(stderr, "ERROR: getting dev info for devstats failed: "
- "%s\n", strerror(-ret));
+ error("getting dev info for devstats 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;
}
@@ -412,9 +404,8 @@ static int cmd_device_stats(int argc, char **argv)
args.flags = flags;
if (ioctl(fdmnt, BTRFS_IOC_GET_DEV_STATS, &args) < 0) {
- fprintf(stderr,
- "ERROR: ioctl(BTRFS_IOC_GET_DEV_STATS) on %s failed: %s\n",
- path, strerror(errno));
+ error("DEV_STATS ioctl failed on %s: %s",
+ path, strerror(errno));
err = 1;
} else {
char *canonical_path;
@@ -461,7 +452,7 @@ out:
static const char * const cmd_device_usage_usage[] = {
"btrfs device usage [options] <path> [<path>..]",
"Show detailed information about internal allocations in devices.",
- HELPINFO_OUTPUT_UNIT_DF,
+ HELPINFO_UNITS_SHORT_LONG,
NULL
};
@@ -498,7 +489,6 @@ static int cmd_device_usage(int argc, char **argv)
{
unsigned unit_mode;
int ret = 0;
- int more_than_one = 0;
int i;
unit_mode = get_unit_mode_from_arg(&argc, argv, 1);
@@ -510,23 +500,22 @@ static int cmd_device_usage(int argc, char **argv)
int fd;
DIR *dirstream = NULL;
- if (more_than_one)
+ if (i > 1)
printf("\n");
fd = btrfs_open_dir(argv[i], &dirstream, 1);
if (fd < 0) {
ret = 1;
- goto out;
+ break;
}
ret = _cmd_device_usage(fd, argv[i], unit_mode);
close_file_or_dir(fd, dirstream);
if (ret)
- goto out;
- more_than_one = 1;
+ break;
}
-out:
+
return !!ret;
}
diff --git a/cmds-fi-usage.c b/cmds-fi-usage.c
index 4b558a8..33bf403 100644
--- a/cmds-fi-usage.c
+++ b/cmds-fi-usage.c
@@ -47,7 +47,7 @@ static int add_info_to_list(struct chunk_info **info_ptr,
for (j = 0 ; j < num_stripes ; j++) {
int i;
- struct chunk_info *p = 0;
+ struct chunk_info *p = NULL;
struct btrfs_stripe *stripe;
u64 devid;
@@ -63,12 +63,12 @@ static int add_info_to_list(struct chunk_info **info_ptr,
}
if (!p) {
- int size = sizeof(struct btrfs_chunk) * (*info_count+1);
- struct chunk_info *res = realloc(*info_ptr, size);
+ int tmp = sizeof(struct btrfs_chunk) * (*info_count + 1);
+ struct chunk_info *res = realloc(*info_ptr, tmp);
if (!res) {
free(*info_ptr);
- fprintf(stderr, "ERROR: not enough memory\n");
+ error("not enough memory");
return -ENOMEM;
}
@@ -161,8 +161,7 @@ static int load_chunk_info(int fd, struct chunk_info **info_ptr, int *info_count
return -e;
if (ret < 0) {
- fprintf(stderr,
- "ERROR: can't perform the search - %s\n",
+ error("cannot look up chunk tree info: %s",
strerror(e));
return 1;
}
@@ -182,7 +181,7 @@ static int load_chunk_info(int fd, struct chunk_info **info_ptr, int *info_count
ret = add_info_to_list(info_ptr, info_count, item);
if (ret) {
- *info_ptr = 0;
+ *info_ptr = NULL;
return 1;
}
@@ -228,12 +227,12 @@ static int cmp_btrfs_ioctl_space_info(const void *a, const void *b)
*/
static struct btrfs_ioctl_space_args *load_space_info(int fd, char *path)
{
- struct btrfs_ioctl_space_args *sargs = 0, *sargs_orig = 0;
- int e, ret, count;
+ struct btrfs_ioctl_space_args *sargs = NULL, *sargs_orig = NULL;
+ int ret, count;
sargs_orig = sargs = calloc(1, sizeof(struct btrfs_ioctl_space_args));
if (!sargs) {
- fprintf(stderr, "ERROR: not enough memory\n");
+ error("not enough memory");
return NULL;
}
@@ -241,11 +240,9 @@ static struct btrfs_ioctl_space_args *load_space_info(int fd, char *path)
sargs->total_spaces = 0;
ret = ioctl(fd, BTRFS_IOC_SPACE_INFO, sargs);
- e = errno;
- if (ret) {
- fprintf(stderr,
- "ERROR: couldn't get space info on '%s' - %s\n",
- path, strerror(e));
+ if (ret < 0) {
+ error("cannot get space info on '%s': %s", path,
+ strerror(errno));
free(sargs);
return NULL;
}
@@ -261,7 +258,7 @@ static struct btrfs_ioctl_space_args *load_space_info(int fd, char *path)
(count * sizeof(struct btrfs_ioctl_space_info)));
if (!sargs) {
free(sargs_orig);
- fprintf(stderr, "ERROR: not enough memory\n");
+ error("not enough memory");
return NULL;
}
@@ -269,12 +266,9 @@ static struct btrfs_ioctl_space_args *load_space_info(int fd, char *path)
sargs->total_spaces = 0;
ret = ioctl(fd, BTRFS_IOC_SPACE_INFO, sargs);
- e = errno;
-
- if (ret) {
- fprintf(stderr,
- "ERROR: couldn't get space info on '%s' - %s\n",
- path, strerror(e));
+ if (ret < 0) {
+ error("cannot get space info with %u slots: %s",
+ count, strerror(errno));
free(sargs);
return NULL;
}
@@ -312,7 +306,7 @@ static int print_filesystem_usage_overall(int fd, struct chunk_info *chunkinfo,
int chunkcount, struct device_info *devinfo, int devcount,
char *path, unsigned unit_mode)
{
- struct btrfs_ioctl_space_args *sargs = 0;
+ struct btrfs_ioctl_space_args *sargs = NULL;
int i;
int ret = 0;
int width = 10; /* default 10 for human units */
@@ -343,6 +337,7 @@ static int print_filesystem_usage_overall(int fd, struct chunk_info *chunkinfo,
u64 free_estimated = 0;
u64 free_min = 0;
int max_data_ratio = 1;
+ int mixed = 0;
sargs = load_space_info(fd, path);
if (!sargs) {
@@ -358,8 +353,7 @@ static int print_filesystem_usage_overall(int fd, struct chunk_info *chunkinfo,
}
if (r_total_size == 0) {
- fprintf(stderr,
- "ERROR: couldn't get space info on '%s' - %s\n",
+ error("cannot get space info on '%s': %s",
path, strerror(errno));
ret = 1;
@@ -391,7 +385,7 @@ static int print_filesystem_usage_overall(int fd, struct chunk_info *chunkinfo,
ratio = 1;
if (!ratio)
- fprintf(stderr, "WARNING: RAID56 detected, not implemented\n");
+ warning("RAID56 detected, not implemented");
if (ratio > max_data_ratio)
max_data_ratio = ratio;
@@ -401,10 +395,9 @@ static int print_filesystem_usage_overall(int fd, struct chunk_info *chunkinfo,
l_global_reserve_used = sargs->spaces[i].used_bytes;
}
if ((flags & (BTRFS_BLOCK_GROUP_DATA | BTRFS_BLOCK_GROUP_METADATA))
- == (BTRFS_BLOCK_GROUP_DATA | BTRFS_BLOCK_GROUP_METADATA)) {
- fprintf(stderr, "WARNING: MIXED blockgroups not handled\n");
+ == (BTRFS_BLOCK_GROUP_DATA | BTRFS_BLOCK_GROUP_METADATA)) {
+ mixed = 1;
}
-
if (flags & BTRFS_BLOCK_GROUP_DATA) {
r_data_used += sargs->spaces[i].used_bytes * ratio;
r_data_chunks += sargs->spaces[i].total_bytes * ratio;
@@ -421,13 +414,20 @@ static int print_filesystem_usage_overall(int fd, struct chunk_info *chunkinfo,
}
}
- r_total_chunks = r_data_chunks + r_metadata_chunks + r_system_chunks;
- r_total_used = r_data_used + r_metadata_used + r_system_used;
+ r_total_chunks = r_data_chunks + r_system_chunks;
+ r_total_used = r_data_used + r_system_used;
+ if (!mixed) {
+ r_total_chunks += r_metadata_chunks;
+ r_total_used += r_metadata_used;
+ }
r_total_unused = r_total_size - r_total_chunks;
/* Raw / Logical = raid factor, >= 1 */
data_ratio = (double)r_data_chunks / l_data_chunks;
- metadata_ratio = (double)r_metadata_chunks / l_metadata_chunks;
+ if (mixed)
+ metadata_ratio = data_ratio;
+ else
+ metadata_ratio = (double)r_metadata_chunks / l_metadata_chunks;
#if 0
/* add the raid5/6 allocated space */
@@ -444,6 +444,13 @@ static int print_filesystem_usage_overall(int fd, struct chunk_info *chunkinfo,
* In non-mixed case there's no difference.
*/
free_estimated = (r_data_chunks - r_data_used) / data_ratio;
+ /*
+ * For mixed-bg the metadata are left out in calculations thus global
+ * reserve would be lost. Part of it could be permanently allocated,
+ * we have to subtract the used bytes so we don't go under zero free.
+ */
+ if (mixed)
+ free_estimated -= l_global_reserve - l_global_reserve_used;
free_min = free_estimated;
/* Chop unallocatable space */
@@ -510,20 +517,20 @@ static int load_device_info(int fd, struct device_info **device_info_ptr,
struct device_info *info;
*device_info_count = 0;
- *device_info_ptr = 0;
+ *device_info_ptr = NULL;
ret = ioctl(fd, BTRFS_IOC_FS_INFO, &fi_args);
if (ret < 0) {
if (errno == EPERM)
return -errno;
- fprintf(stderr, "ERROR: cannot get filesystem info - %s\n",
+ error("cannot get filesystem info: %s",
strerror(errno));
return 1;
}
info = calloc(fi_args.num_devices, sizeof(struct device_info));
if (!info) {
- fprintf(stderr, "ERROR: not enough memory\n");
+ error("not enough memory");
return 1;
}
@@ -535,9 +542,7 @@ static int load_device_info(int fd, struct device_info **device_info_ptr,
if (ret == -ENODEV)
continue;
if (ret) {
- fprintf(stderr,
- "ERROR: cannot get info about device devid=%d\n",
- i);
+ error("cannot get info about device devid=%d", i);
free(info);
return ret;
}
@@ -571,16 +576,16 @@ int load_chunk_and_device_info(int fd, struct chunk_info **chunkinfo,
ret = load_chunk_info(fd, chunkinfo, chunkcount);
if (ret == -EPERM) {
- fprintf(stderr,
- "WARNING: can't read detailed chunk info, RAID5/6 numbers will be incorrect, run as root\n");
+ warning(
+"cannot read detailed chunk info, RAID5/6 numbers will be incorrect, run as root");
} else if (ret) {
return ret;
}
ret = load_device_info(fd, devinfo, devcount);
if (ret == -EPERM) {
- fprintf(stderr,
- "WARNING: can't get filesystem info from ioctl(FS_INFO), run as root\n");
+ warning(
+ "cannot get filesystem info from ioctl(FS_INFO), run as root");
ret = 0;
}
@@ -620,7 +625,7 @@ static void _cmd_filesystem_usage_tabular(unsigned unit_mode,
{
int i;
u64 total_unused = 0;
- struct string_table *matrix = 0;
+ struct string_table *matrix = NULL;
int ncols, nrows;
int col;
int unallocated_col;
@@ -642,7 +647,7 @@ static void _cmd_filesystem_usage_tabular(unsigned unit_mode,
matrix = table_create(ncols, nrows);
if (!matrix) {
- fprintf(stderr, "ERROR: not enough memory\n");
+ error("not enough memory");
return;
}
@@ -902,7 +907,7 @@ out:
const char * const cmd_filesystem_usage_usage[] = {
"btrfs filesystem usage [options] <path> [<path>..]",
"Show detailed information about internal filesystem usage .",
- HELPINFO_OUTPUT_UNIT_DF,
+ HELPINFO_UNITS_SHORT_LONG,
"-T show data in tabular format",
NULL
};
diff --git a/cmds-filesystem.c b/cmds-filesystem.c
index 819daa1..4c6e856 100644
--- a/cmds-filesystem.c
+++ b/cmds-filesystem.c
@@ -123,14 +123,14 @@ static const char * const filesystem_cmd_group_usage[] = {
static const char * const cmd_filesystem_df_usage[] = {
"btrfs filesystem df [options] <path>",
"Show space usage information for a mount point",
- HELPINFO_OUTPUT_UNIT_DF,
+ HELPINFO_UNITS_SHORT_LONG,
NULL
};
static int get_df(int fd, struct btrfs_ioctl_space_args **sargs_ret)
{
u64 count = 0;
- int ret, e;
+ int ret;
struct btrfs_ioctl_space_args *sargs;
sargs = malloc(sizeof(struct btrfs_ioctl_space_args));
@@ -141,12 +141,10 @@ static int get_df(int fd, struct btrfs_ioctl_space_args **sargs_ret)
sargs->total_spaces = 0;
ret = ioctl(fd, BTRFS_IOC_SPACE_INFO, sargs);
- e = errno;
- if (ret) {
- fprintf(stderr, "ERROR: couldn't get space info - %s\n",
- strerror(e));
+ if (ret < 0) {
+ error("cannot get space info: %s\n", strerror(errno));
free(sargs);
- return -e;
+ return -errno;
}
/* This really should never happen */
if (!sargs->total_spaces) {
@@ -164,12 +162,11 @@ static int get_df(int fd, struct btrfs_ioctl_space_args **sargs_ret)
sargs->space_slots = count;
sargs->total_spaces = 0;
ret = ioctl(fd, BTRFS_IOC_SPACE_INFO, sargs);
- e = errno;
- if (ret) {
- fprintf(stderr, "ERROR: get space info count %llu - %s\n",
- count, strerror(e));
+ if (ret < 0) {
+ error("cannot get space info with %llu slots: %s",
+ count, strerror(errno));
free(sargs);
- return -e;
+ return -errno;
}
*sargs_ret = sargs;
return 0;
@@ -215,7 +212,7 @@ static int cmd_filesystem_df(int argc, char **argv)
print_df(sargs, unit_mode);
free(sargs);
} else {
- fprintf(stderr, "ERROR: get_df failed %s\n", strerror(-ret));
+ error("get_df failed %s", strerror(-ret));
}
close_file_or_dir(fd, dirstream);
@@ -233,7 +230,7 @@ static int match_search_item_kernel(__u8 *fsid, char *mnt, char *label,
if (!strncmp(uuidbuf, search, search_len))
return 1;
- if (strlen(label) && strcmp(label, search) == 0)
+ if (*label && strcmp(label, search) == 0)
return 1;
if (strcmp(mnt, search) == 0)
@@ -387,7 +384,7 @@ static u64 calc_used_bytes(struct btrfs_ioctl_space_args *si)
static int print_one_fs(struct btrfs_ioctl_fs_info_args *fs_info,
struct btrfs_ioctl_dev_info_args *dev_info,
struct btrfs_ioctl_space_args *space_info,
- char *label, char *path, unsigned unit_mode)
+ char *label, unsigned unit_mode)
{
int i;
int fd;
@@ -403,7 +400,7 @@ static int print_one_fs(struct btrfs_ioctl_fs_info_args *fs_info,
return ret;
uuid_unparse(fs_info->fsid, uuidbuf);
- if (label && strlen(label))
+ if (label && *label)
printf("Label: '%s' ", label);
else
printf("Label: none ");
@@ -491,8 +488,7 @@ static int btrfs_scan_kernel(void *search, unsigned unit_mode)
fd = open(mnt->mnt_dir, O_RDONLY);
if ((fd != -1) && !get_df(fd, &space_info_arg)) {
print_one_fs(&fs_info_arg, dev_info_arg,
- space_info_arg, label, mnt->mnt_dir,
- unit_mode);
+ space_info_arg, label, unit_mode);
kfree(space_info_arg);
memset(label, 0, sizeof(label));
found = 1;
@@ -511,18 +507,13 @@ out:
static int dev_to_fsid(char *dev, __u8 *fsid)
{
struct btrfs_super_block *disk_super;
- char *buf;
+ char buf[BTRFS_SUPER_INFO_SIZE];
int ret;
int fd;
- buf = malloc(4096);
- if (!buf)
- return -ENOMEM;
-
fd = open(dev, O_RDONLY);
if (fd < 0) {
ret = -errno;
- free(buf);
return ret;
}
@@ -537,7 +528,6 @@ static int dev_to_fsid(char *dev, __u8 *fsid)
out:
close(fd);
- free(buf);
return ret;
}
@@ -772,7 +762,7 @@ static const char * const cmd_filesystem_show_usage[] = {
"Show the structure of a filesystem",
"-d|--all-devices show only disks under /dev containing btrfs filesystem",
"-m|--mounted show only mounted btrfs",
- HELPINFO_OUTPUT_UNIT,
+ HELPINFO_UNITS_LONG,
"If no argument is given, structure of all present filesystems is shown.",
NULL
};
@@ -823,7 +813,7 @@ static int cmd_filesystem_show(int argc, char **argv)
if (argc > optind) {
search = argv[optind];
- if (strlen(search) == 0)
+ if (*search == 0)
usage(cmd_filesystem_show_usage);
type = check_arg_type(search);
@@ -851,9 +841,7 @@ static int cmd_filesystem_show(int argc, char **argv)
} else {
ret = dev_to_fsid(search, fsid);
if (ret) {
- fprintf(stderr,
- "ERROR: No btrfs on %s\n",
- search);
+ error("no btrfs on %s", search);
return 1;
}
uuid_unparse(fsid, uuid_buf);
@@ -882,14 +870,13 @@ devs_only:
ret = btrfs_scan_lblkid();
if (ret) {
- fprintf(stderr, "ERROR: %d while scanning\n", ret);
+ error("blkid device scan returned %d\n", ret);
return 1;
}
ret = search_umounted_fs_uuids(&all_uuids, search, &found);
if (ret < 0) {
- fprintf(stderr,
- "ERROR: %d while searching target device\n", ret);
+ error("searching target device returned error %d", ret);
return 1;
}
@@ -899,8 +886,7 @@ devs_only:
*/
ret = map_seed_devices(&all_uuids);
if (ret) {
- fprintf(stderr,
- "ERROR: %d while mapping seed devices\n", ret);
+ error("mapping seed devices returned error %d", ret);
return 1;
}
@@ -916,7 +902,6 @@ devs_only:
free_fs_devices(fs_devices);
}
out:
- printf("%s\n", PACKAGE_STRING);
free_seen_fsid();
return ret;
}
@@ -947,8 +932,7 @@ static int cmd_filesystem_sync(int argc, char **argv)
e = errno;
close_file_or_dir(fd, dirstream);
if( res < 0 ){
- fprintf(stderr, "ERROR: unable to fs-syncing '%s' - %s\n",
- path, strerror(e));
+ error("sync ioctl failed on '%s': %s", path, strerror(e));
return 1;
}
@@ -962,7 +946,7 @@ static int parse_compress_type(char *s)
else if (strcmp(optarg, "lzo") == 0)
return BTRFS_COMPRESS_LZO;
else {
- fprintf(stderr, "Unknown compress type %s\n", s);
+ error("unknown compression type %s", s);
exit(1);
};
}
@@ -1009,16 +993,15 @@ static int defrag_callback(const char *fpath, const struct stat *sb,
if (defrag_global_verbose)
printf("%s\n", fpath);
fd = open(fpath, O_RDWR);
- e = errno;
if (fd < 0)
goto error;
ret = do_defrag(fd, defrag_global_fancy_ioctl, &defrag_global_range);
e = errno;
close(fd);
if (ret && e == ENOTTY && defrag_global_fancy_ioctl) {
- fprintf(stderr, "ERROR: defrag range ioctl not "
+ error("defrag range ioctl not "
"supported in this kernel, please try "
- "without any options.\n");
+ "without any options.");
defrag_global_errors++;
return ENOTTY;
}
@@ -1028,7 +1011,7 @@ static int defrag_callback(const char *fpath, const struct stat *sb,
return 0;
error:
- fprintf(stderr, "ERROR: defrag failed on %s - %s\n", fpath, strerror(e));
+ error("defrag failed on %s: %s", fpath, strerror(e));
defrag_global_errors++;
return 0;
}
@@ -1082,8 +1065,8 @@ static int cmd_filesystem_defrag(int argc, char **argv)
case 't':
thresh = parse_size(optarg);
if (thresh > (u32)-1) {
- fprintf(stderr,
- "WARNING: target extent size %llu too big, trimmed to %u\n",
+ warning(
+ "target extent size %llu too big, trimmed to %u",
thresh, (u32)-1);
thresh = (u32)-1;
}
@@ -1117,23 +1100,22 @@ static int cmd_filesystem_defrag(int argc, char **argv)
dirstream = NULL;
fd = open_file_or_dir(argv[i], &dirstream);
if (fd < 0) {
- fprintf(stderr, "ERROR: failed to open %s - %s\n", argv[i],
+ error("cannot open %s: %s\n", argv[i],
strerror(errno));
defrag_global_errors++;
close_file_or_dir(fd, dirstream);
continue;
}
if (fstat(fd, &st)) {
- fprintf(stderr, "ERROR: failed to stat %s - %s\n",
+ error("failed to stat %s: %s",
argv[i], strerror(errno));
defrag_global_errors++;
close_file_or_dir(fd, dirstream);
continue;
}
if (!(S_ISDIR(st.st_mode) || S_ISREG(st.st_mode))) {
- fprintf(stderr,
- "ERROR: %s is not a directory or a regular file\n",
- argv[i]);
+ error("%s is not a directory or a regular file\n",
+ argv[i]);
defrag_global_errors++;
close_file_or_dir(fd, dirstream);
continue;
@@ -1162,20 +1144,17 @@ static int cmd_filesystem_defrag(int argc, char **argv)
}
close_file_or_dir(fd, dirstream);
if (ret && e == ENOTTY && defrag_global_fancy_ioctl) {
- fprintf(stderr, "ERROR: defrag range ioctl not "
+ error("defrag range ioctl not "
"supported in this kernel, please try "
- "without any options.\n");
+ "without any options.");
defrag_global_errors++;
break;
}
if (ret) {
- fprintf(stderr, "ERROR: defrag failed on %s - %s\n",
- argv[i], strerror(e));
+ error("defrag failed on %s: %s", argv[i], strerror(e));
defrag_global_errors++;
}
}
- if (defrag_global_verbose)
- printf("%s\n", PACKAGE_STRING);
if (defrag_global_errors)
fprintf(stderr, "total %d failures\n", defrag_global_errors);
@@ -1207,20 +1186,17 @@ static int cmd_filesystem_resize(int argc, char **argv)
len = strlen(amount);
if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
- fprintf(stderr, "ERROR: size value too long ('%s)\n",
- amount);
+ error("resize value too long (%s)", amount);
return 1;
}
res = stat(path, &st);
if (res < 0) {
- fprintf(stderr, "ERROR: resize: cannot stat %s: %s\n",
- path, strerror(errno));
+ error("resize: cannot stat %s: %s", path, strerror(errno));
return 1;
}
if (!S_ISDIR(st.st_mode)) {
- fprintf(stderr,
- "ERROR: resize works on mounted filesystems and accepts only\n"
+ error("resize works on mounted filesystems and accepts only\n"
"directories as argument. Passing file containing a btrfs image\n"
"would resize the underlying filesystem instead of the image.\n");
return 1;
@@ -1239,12 +1215,11 @@ static int cmd_filesystem_resize(int argc, char **argv)
if( res < 0 ){
switch (e) {
case EFBIG:
- fprintf(stderr, "ERROR: unable to resize '%s' - no enouth free space\n",
+ error("unable to resize '%s': no enough free space",
path);
break;
default:
- fprintf(stderr, "ERROR: unable to resize '%s' - %s\n",
- path, strerror(e));
+ error("unable to resize '%s': %s", path, strerror(e));
break;
}
return 1;
@@ -1252,11 +1227,9 @@ static int cmd_filesystem_resize(int argc, char **argv)
const char *err_str = btrfs_err_str(res);
if (err_str) {
- fprintf(stderr, "ERROR: btrfs error resizing '%s' - %s\n",
- path, err_str);
+ error("resizing of '%s' failed: %s", path, err_str);
} else {
- fprintf(stderr,
- "ERROR: btrfs error resizing '%s' - unknown btrfs_err_code %d\n",
+ error("resizing of '%s' failed: unknown error %d",
path, res);
}
return 1;
diff --git a/cmds-inspect.c b/cmds-inspect.c
index 40ab49b..7fa4881 100644
--- a/cmds-inspect.c
+++ b/cmds-inspect.c
@@ -42,19 +42,15 @@ static int __ino_to_path_fd(u64 inum, int fd, int verbose, const char *prepend)
int ret;
int i;
struct btrfs_ioctl_ino_path_args ipa;
- struct btrfs_data_container *fspath;
-
- fspath = malloc(4096);
- if (!fspath)
- return -ENOMEM;
+ struct btrfs_data_container fspath[PATH_MAX];
memset(fspath, 0, sizeof(*fspath));
ipa.inum = inum;
- ipa.size = 4096;
+ ipa.size = PATH_MAX;
ipa.fspath = ptr_to_u64(fspath);
ret = ioctl(fd, BTRFS_IOC_INO_PATHS, &ipa);
- if (ret) {
+ if (ret < 0) {
printf("ioctl ret=%d, error: %s\n", ret, strerror(errno));
goto out;
}
@@ -79,7 +75,6 @@ static int __ino_to_path_fd(u64 inum, int fd, int verbose, const char *prepend)
}
out:
- free(fspath);
return !!ret;
}
@@ -194,7 +189,7 @@ static int cmd_inspect_logical_resolve(int argc, char **argv)
}
ret = ioctl(fd, BTRFS_IOC_LOGICAL_INO, &loi);
- if (ret) {
+ if (ret < 0) {
printf("ioctl ret=%d, error: %s\n", ret, strerror(errno));
goto out;
}
@@ -334,6 +329,14 @@ out:
return !!ret;
}
+static const char* const cmd_inspect_min_dev_size_usage[] = {
+ "btrfs inspect-internal min-dev-size [options] <path>",
+ "Get the minimum size the device can be shrunk to. The",
+ "device id 1 is used by default.",
+ "--id DEVID specify the device id to query",
+ NULL
+};
+
struct dev_extent_elem {
u64 start;
/* inclusive end */
@@ -574,14 +577,6 @@ out:
return ret;
}
-static const char* const cmd_inspect_min_dev_size_usage[] = {
- "btrfs inspect-internal min-dev-size [options] <path>",
- "Get the minimum size the device can be shrunk to. The",
- "device id 1 is used by default.",
- "--id DEVID specify the device id to query",
- NULL
-};
-
static int cmd_inspect_min_dev_size(int argc, char **argv)
{
int ret;
diff --git a/cmds-property.c b/cmds-property.c
index 0ffd250..b7b2484 100644
--- a/cmds-property.c
+++ b/cmds-property.c
@@ -32,35 +32,6 @@ static const char * const property_cmd_group_usage[] = {
NULL
};
-static const char * const cmd_property_get_usage[] = {
- "btrfs property get [-t <type>] <object> [<name>]",
- "Gets a property from a btrfs object.",
- "If no name is specified, all properties for the given object are",
- "printed.",
- "A filesystem object can be a the filesystem itself, a subvolume,",
- "an inode or a device. The '-t <type>' option can be used to explicitly",
- "specify what type of object you meant. This is only needed when a",
- "property could be set for more then one object type. Possible types",
- "are s[ubvol], f[ilesystem], i[node] and d[evice].",
- NULL
-};
-
-static const char * const cmd_property_set_usage[] = {
- "btrfs property set [-t <type>] <object> <name> <value>",
- "Sets a property on a btrfs object.",
- "Please see the help of 'btrfs property get' for a description of",
- "objects and object types.",
- NULL
-};
-
-static const char * const cmd_property_list_usage[] = {
- "btrfs property list [-t <type>] <object>",
- "Lists available properties with their descriptions for the given object.",
- "Please see the help of 'btrfs property get' for a description of",
- "objects and object types.",
- NULL
-};
-
static int parse_prop(const char *arg, const struct prop_handler *props,
const struct prop_handler **prop_ret)
{
@@ -86,7 +57,7 @@ static int get_fsid(const char *path, u8 *fsid, int silent)
if (fd < 0) {
ret = -errno;
if (!silent)
- fprintf(stderr, "ERROR: open %s failed. %s\n", path,
+ error("failed to open %s: %s", path,
strerror(-ret));
goto out;
}
@@ -149,8 +120,7 @@ static int check_is_root(const char *object)
ret = get_fsid(object, fsid, 0);
if (ret < 0) {
- fprintf(stderr, "ERROR: get_fsid for %s failed. %s\n", object,
- strerror(-ret));
+ error("get_fsid for %s failed: %s", object, strerror(-ret));
goto out;
}
@@ -162,8 +132,7 @@ static int check_is_root(const char *object)
ret = 1;
goto out;
} else if (ret < 0) {
- fprintf(stderr, "ERROR: get_fsid for %s failed. %s\n", tmp,
- strerror(-ret));
+ error("get_fsid for %s failed: %s", tmp, strerror(-ret));
goto out;
}
@@ -285,28 +254,26 @@ static int setget_prop(int types, const char *object,
ret = parse_prop(name, prop_handlers, &prop);
if (ret == -1) {
- fprintf(stderr, "ERROR: property is unknown\n");
+ error("unknown property: %s", name);
ret = 40;
goto out;
}
types &= prop->types;
if (!types) {
- fprintf(stderr,
- "ERROR: object is not compatible with property\n");
+ error("object is not compatible with property: %s", prop->name);
ret = 47;
goto out;
}
if (count_bits(types) > 1) {
- fprintf(stderr,
- "ERROR: type of object is ambiguous. Please specify a type by hand.\n");
+ error("type of object is ambiguous, please use option -t");
ret = 48;
goto out;
}
if (value && prop->read_only) {
- fprintf(stderr, "ERROR: %s is a read-only property.\n",
+ error("property is read-only property: %s",
prop->name);
ret = 51;
goto out;
@@ -361,7 +328,7 @@ static void parse_args(int argc, char **argv,
!strcmp(type_str, "device")) {
*types = prop_object_dev;
} else {
- fprintf(stderr, "ERROR: invalid object type.\n");
+ error("invalid object type: %s", type_str);
usage(usage_str);
}
}
@@ -374,26 +341,37 @@ static void parse_args(int argc, char **argv,
*value = argv[optind++];
if (optind != argc) {
- fprintf(stderr, "ERROR: invalid arguments.\n");
+ error("unexpected agruments found");
usage(usage_str);
}
if (!*types && object && *object) {
ret = autodetect_object_types(*object, types);
if (ret < 0) {
- fprintf(stderr,
- "ERROR: failed to detect object type. %s\n",
+ error("failed to detect object type: %s",
strerror(-ret));
usage(usage_str);
}
if (!*types) {
- fprintf(stderr,
- "ERROR: object is not a btrfs object.\n");
+ error("object is not a btrfs object: %s", *object);
usage(usage_str);
}
}
}
+static const char * const cmd_property_get_usage[] = {
+ "btrfs property get [-t <type>] <object> [<name>]",
+ "Gets a property from a btrfs object.",
+ "If no name is specified, all properties for the given object are",
+ "printed.",
+ "A filesystem object can be a the filesystem itself, a subvolume,",
+ "an inode or a device. The '-t <type>' option can be used to explicitly",
+ "specify what type of object you meant. This is only needed when a",
+ "property could be set for more then one object type. Possible types",
+ "are s[ubvol], f[ilesystem], i[node] and d[evice].",
+ NULL
+};
+
static int cmd_property_get(int argc, char **argv)
{
int ret;
@@ -407,8 +385,8 @@ static int cmd_property_get(int argc, char **argv)
parse_args(argc, argv, cmd_property_get_usage, &types, &object, &name,
NULL);
if (!object) {
- fprintf(stderr, "ERROR: invalid arguments.\n");
- usage(cmd_property_set_usage);
+ error("invalid arguments");
+ usage(cmd_property_get_usage);
}
if (name)
@@ -419,6 +397,14 @@ static int cmd_property_get(int argc, char **argv)
return ret;
}
+static const char * const cmd_property_set_usage[] = {
+ "btrfs property set [-t <type>] <object> <name> <value>",
+ "Sets a property on a btrfs object.",
+ "Please see the help of 'btrfs property get' for a description of",
+ "objects and object types.",
+ NULL
+};
+
static int cmd_property_set(int argc, char **argv)
{
int ret;
@@ -433,7 +419,7 @@ static int cmd_property_set(int argc, char **argv)
parse_args(argc, argv, cmd_property_set_usage, &types,
&object, &name, &value);
if (!object || !name || !value) {
- fprintf(stderr, "ERROR: invalid arguments.\n");
+ error("invalid arguments");
usage(cmd_property_set_usage);
}
@@ -442,6 +428,14 @@ static int cmd_property_set(int argc, char **argv)
return ret;
}
+static const char * const cmd_property_list_usage[] = {
+ "btrfs property list [-t <type>] <object>",
+ "Lists available properties with their descriptions for the given object.",
+ "Please see the help of 'btrfs property get' for a description of",
+ "objects and object types.",
+ NULL
+};
+
static int cmd_property_list(int argc, char **argv)
{
int ret;
@@ -454,8 +448,8 @@ static int cmd_property_list(int argc, char **argv)
parse_args(argc, argv, cmd_property_list_usage,
&types, &object, NULL, NULL);
if (!object) {
- fprintf(stderr, "ERROR: invalid arguments.\n");
- usage(cmd_property_set_usage);
+ error("invalid arguments");
+ usage(cmd_property_list_usage);
}
ret = dump_props(types, object, 1);
diff --git a/cmds-qgroup.c b/cmds-qgroup.c
index a64b716..db5ee21 100644
--- a/cmds-qgroup.c
+++ b/cmds-qgroup.c
@@ -36,7 +36,6 @@ static int qgroup_assign(int assign, int argc, char **argv)
{
int ret = 0;
int fd;
- int e;
int rescan = 0;
char *path;
struct btrfs_ioctl_qgroup_assign_args args;
@@ -76,7 +75,7 @@ static int qgroup_assign(int assign, int argc, char **argv)
* FIXME src should accept subvol path
*/
if (btrfs_qgroup_level(args.src) >= btrfs_qgroup_level(args.dst)) {
- fprintf(stderr, "ERROR: bad relation requested '%s'\n", path);
+ error("bad relation requested: %s", path);
return 1;
}
fd = btrfs_open_dir(path, &dirstream, 1);
@@ -84,10 +83,8 @@ static int qgroup_assign(int assign, int argc, char **argv)
return 1;
ret = ioctl(fd, BTRFS_IOC_QGROUP_ASSIGN, &args);
- e = errno;
if (ret < 0) {
- fprintf(stderr, "ERROR: unable to assign quota group: %s\n",
- strerror(e));
+ error("unable to assign quota group: %s", strerror(errno));
close_file_or_dir(fd, dirstream);
return 1;
}
@@ -102,17 +99,16 @@ static int qgroup_assign(int assign, int argc, char **argv)
*/
if (ret > 0) {
if (rescan) {
- struct btrfs_ioctl_quota_rescan_args args;
+ struct btrfs_ioctl_quota_rescan_args qargs;
printf("Quota data changed, rescan scheduled\n");
- memset(&args, 0, sizeof(args));
- ret = ioctl(fd, BTRFS_IOC_QUOTA_RESCAN, &args);
+ memset(&qargs, 0, sizeof(qargs));
+ ret = ioctl(fd, BTRFS_IOC_QUOTA_RESCAN, &qargs);
if (ret < 0)
- fprintf(stderr,
- "ERROR: quota rescan failed: %s\n",
+ error("quota rescan failed: %s",
strerror(errno));
} else {
- printf("WARNING: quotas may be inconsistent, rescan needed\n");
+ warning("quotas may be inconsistent, rescan needed");
}
}
close_file_or_dir(fd, dirstream);
@@ -143,7 +139,7 @@ static int qgroup_create(int create, int argc, char **argv)
e = errno;
close_file_or_dir(fd, dirstream);
if (ret < 0) {
- fprintf(stderr, "ERROR: unable to %s quota group: %s\n",
+ error("unable to %s quota group: %s",
create ? "create":"destroy", strerror(e));
return 1;
}
@@ -271,7 +267,7 @@ static const char * const cmd_qgroup_show_usage[] = {
" (including ancestral qgroups)",
"-f list all qgroups which impact the given path",
" (excluding ancestral qgroups)",
- HELPINFO_OUTPUT_UNIT,
+ HELPINFO_UNITS_LONG,
"--sort=qgroupid,rfer,excl,max_rfer,max_excl",
" list qgroups sorted by specified items",
" you can use '+' or '-' in front of each item.",
@@ -369,8 +365,7 @@ static int cmd_qgroup_show(int argc, char **argv)
e = errno;
close_file_or_dir(fd, dirstream);
if (ret < 0)
- fprintf(stderr, "ERROR: can't list qgroups: %s\n",
- strerror(e));
+ error("can't list qgroups: %s", strerror(e));
return !!ret;
}
@@ -418,7 +413,7 @@ static int cmd_qgroup_limit(int argc, char **argv)
usage(cmd_qgroup_limit_usage);
if (!parse_limit(argv[optind], &size)) {
- fprintf(stderr, "Invalid size argument given\n");
+ error("invalid size argument: %s", argv[optind]);
return 1;
}
@@ -439,12 +434,11 @@ static int cmd_qgroup_limit(int argc, char **argv)
path = argv[optind + 1];
ret = test_issubvolume(path);
if (ret < 0) {
- fprintf(stderr, "ERROR: error accessing '%s'\n", path);
+ error("cannot access '%s': %s", path, strerror(-ret));
return 1;
}
if (!ret) {
- fprintf(stderr, "ERROR: '%s' is not a subvolume\n",
- path);
+ error("'%s' is not a subvolume", path);
return 1;
}
/*
@@ -465,8 +459,7 @@ static int cmd_qgroup_limit(int argc, char **argv)
e = errno;
close_file_or_dir(fd, dirstream);
if (ret < 0) {
- fprintf(stderr, "ERROR: unable to limit requested quota group: "
- "%s\n", strerror(e));
+ error("unable to limit requested quota group: %s", strerror(e));
return 1;
}
return 0;
diff --git a/cmds-quota.c b/cmds-quota.c
index efbc3ef..34b8dac 100644
--- a/cmds-quota.c
+++ b/cmds-quota.c
@@ -53,8 +53,7 @@ static int quota_ctl(int cmd, int argc, char **argv)
e = errno;
close_file_or_dir(fd, dirstream);
if (ret < 0) {
- fprintf(stderr, "ERROR: quota command failed: %s\n",
- strerror(e));
+ error("quota command failed: %s", strerror(e));
return 1;
}
return 0;
@@ -129,7 +128,7 @@ static int cmd_quota_rescan(int argc, char **argv)
}
if (ioctlnum != BTRFS_IOC_QUOTA_RESCAN && wait_for_completion) {
- fprintf(stderr, "ERROR: -w cannot be used with -s\n");
+ error("switch -w cannot be used with -s");
return 1;
}
@@ -154,8 +153,7 @@ static int cmd_quota_rescan(int argc, char **argv)
if (ioctlnum == BTRFS_IOC_QUOTA_RESCAN) {
if (ret < 0) {
- fprintf(stderr, "ERROR: quota rescan failed: "
- "%s\n", strerror(e));
+ error("quota rescan failed: %s", strerror(e));
return 1;
} else {
printf("quota rescan started\n");
diff --git a/cmds-receive.c b/cmds-receive.c
index 1d7d897..cbb1642 100644
--- a/cmds-receive.c
+++ b/cmds-receive.c
@@ -101,7 +101,7 @@ static int finish_subvol(struct btrfs_receive *r)
O_RDONLY | O_NOATIME);
if (subvol_fd < 0) {
ret = -errno;
- fprintf(stderr, "ERROR: open %s failed. %s\n",
+ error("cannot open %s: %s\n",
r->cur_subvol_path, strerror(-ret));
goto out;
}
@@ -119,7 +119,7 @@ static int finish_subvol(struct btrfs_receive *r)
ret = ioctl(subvol_fd, BTRFS_IOC_SET_RECEIVED_SUBVOL, &rs_args);
if (ret < 0) {
ret = -errno;
- fprintf(stderr, "ERROR: BTRFS_IOC_SET_RECEIVED_SUBVOL failed. %s\n",
+ error("ioctl BTRFS_IOC_SET_RECEIVED_SUBVOL failed: %s",
strerror(-ret));
goto out;
}
@@ -128,7 +128,7 @@ static int finish_subvol(struct btrfs_receive *r)
ret = ioctl(subvol_fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags);
if (ret < 0) {
ret = -errno;
- fprintf(stderr, "ERROR: BTRFS_IOC_SUBVOL_GETFLAGS failed. %s\n",
+ error("ioctl BTRFS_IOC_SUBVOL_GETFLAGS failed: %s",
strerror(-ret));
goto out;
}
@@ -138,8 +138,8 @@ static int finish_subvol(struct btrfs_receive *r)
ret = ioctl(subvol_fd, BTRFS_IOC_SUBVOL_SETFLAGS, &flags);
if (ret < 0) {
ret = -errno;
- fprintf(stderr, "ERROR: failed to make subvolume read only. "
- "%s\n", strerror(-ret));
+ error("failed to make subvolume read only: %s",
+ strerror(-ret));
goto out;
}
@@ -169,20 +169,19 @@ static int process_subvol(const char *path, const u8 *uuid, u64 ctransid,
BUG_ON(r->cur_subvol.path);
BUG_ON(r->cur_subvol_path[0]);
- if (strlen(r->dest_dir_path) == 0) {
+ if (*r->dest_dir_path == 0) {
strncpy_null(r->cur_subvol_path, path);
} else {
ret = path_cat_out(r->cur_subvol_path, r->dest_dir_path, path);
if (ret < 0) {
- fprintf(stderr, "ERROR: subvol: path invalid: %s\n",
- path);
+ error("subvol: path invalid: %s\n", path);
goto out;
}
}
ret = path_cat3_out(r->full_subvol_path, r->root_path,
r->dest_dir_path, path);
if (ret < 0) {
- fprintf(stderr, "ERROR: subvol: path invalid: %s\n", path);
+ error("subvol: path invalid: %s", path);
goto out;
}
@@ -203,8 +202,7 @@ static int process_subvol(const char *path, const u8 *uuid, u64 ctransid,
ret = ioctl(r->dest_dir_fd, BTRFS_IOC_SUBVOL_CREATE, &args_v1);
if (ret < 0) {
ret = -errno;
- fprintf(stderr, "ERROR: creating subvolume %s failed. "
- "%s\n", path, strerror(-ret));
+ error("creating subvolume %s failed: %s", path, strerror(-ret));
goto out;
}
@@ -229,20 +227,19 @@ static int process_snapshot(const char *path, const u8 *uuid, u64 ctransid,
BUG_ON(r->cur_subvol.path);
BUG_ON(r->cur_subvol_path[0]);
- if (strlen(r->dest_dir_path) == 0) {
+ if (*r->dest_dir_path == 0) {
strncpy_null(r->cur_subvol_path, path);
} else {
ret = path_cat_out(r->cur_subvol_path, r->dest_dir_path, path);
if (ret < 0) {
- fprintf(stderr, "ERROR: snapshot: path invalid: %s\n",
- path);
+ error("snapshot: path invalid: %s", path);
goto out;
}
}
ret = path_cat3_out(r->full_subvol_path, r->root_path,
r->dest_dir_path, path);
if (ret < 0) {
- fprintf(stderr, "ERROR: snapshot: path invalid: %s\n", path);
+ error("snapshot: path invalid: %s", path);
goto out;
}
@@ -272,7 +269,7 @@ static int process_snapshot(const char *path, const u8 *uuid, u64 ctransid,
}
if (!parent_subvol) {
ret = -ENOENT;
- fprintf(stderr, "ERROR: could not find parent subvolume\n");
+ error("cannot find parent subvolume");
goto out;
}
@@ -291,8 +288,8 @@ static int process_snapshot(const char *path, const u8 *uuid, u64 ctransid,
/* First make sure the parent subvol is actually in our path */
if (sub_len < root_len ||
strstr(parent_subvol->path, r->full_root_path) == NULL) {
- fprintf(stderr, "ERROR: parent subvol is not reachable"
- " from inside the root subvol.\n");
+ error(
+ "parent subvol is not reachable from inside the root subvol");
ret = -ENOENT;
goto out;
}
@@ -326,7 +323,7 @@ static int process_snapshot(const char *path, const u8 *uuid, u64 ctransid,
}
}*/
- if (strlen(parent_subvol->path) == 0)
+ if (*parent_subvol->path == 0)
args_v2.fd = dup(r->mnt_fd);
else
args_v2.fd = openat(r->mnt_fd, parent_subvol->path,
@@ -334,7 +331,7 @@ static int process_snapshot(const char *path, const u8 *uuid, u64 ctransid,
if (args_v2.fd < 0) {
ret = -errno;
if (errno != ENOENT)
- fprintf(stderr, "ERROR: open %s failed. %s\n",
+ error("cannot open %s: %s",
parent_subvol->path, strerror(-ret));
else
fprintf(stderr,
@@ -349,9 +346,8 @@ static int process_snapshot(const char *path, const u8 *uuid, u64 ctransid,
close(args_v2.fd);
if (ret < 0) {
ret = -errno;
- fprintf(stderr, "ERROR: creating snapshot %s -> %s "
- "failed. %s\n", parent_subvol->path,
- path, strerror(-ret));
+ error("creating snapshot %s -> %s failed: %s",
+ parent_subvol->path, path, strerror(-ret));
goto out;
}
@@ -371,7 +367,7 @@ static int process_mkfile(const char *path, void *user)
ret = path_cat_out(full_path, r->full_subvol_path, path);
if (ret < 0) {
- fprintf(stderr, "ERROR: mkfile: path invalid: %s\n", path);
+ error("mkfile: path invalid: %s", path);
goto out;
}
@@ -381,8 +377,7 @@ static int process_mkfile(const char *path, void *user)
ret = creat(full_path, 0600);
if (ret < 0) {
ret = -errno;
- fprintf(stderr, "ERROR: mkfile %s failed. %s\n", path,
- strerror(-ret));
+ error("mkfile %s failed: %s", path, strerror(-ret));
goto out;
}
close(ret);
@@ -400,7 +395,7 @@ static int process_mkdir(const char *path, void *user)
ret = path_cat_out(full_path, r->full_subvol_path, path);
if (ret < 0) {
- fprintf(stderr, "ERROR: mkdir: path invalid: %s\n", path);
+ error("mkdir: path invalid: %s", path);
goto out;
}
@@ -410,8 +405,7 @@ static int process_mkdir(const char *path, void *user)
ret = mkdir(full_path, 0700);
if (ret < 0) {
ret = -errno;
- fprintf(stderr, "ERROR: mkdir %s failed. %s\n", path,
- strerror(-ret));
+ error("mkdir %s failed: %s", path, strerror(-ret));
}
out:
@@ -426,7 +420,7 @@ static int process_mknod(const char *path, u64 mode, u64 dev, void *user)
ret = path_cat_out(full_path, r->full_subvol_path, path);
if (ret < 0) {
- fprintf(stderr, "ERROR: mknod: path invalid: %s\n", path);
+ error("mknod: path invalid: %s", path);
goto out;
}
@@ -437,8 +431,7 @@ static int process_mknod(const char *path, u64 mode, u64 dev, void *user)
ret = mknod(full_path, mode & S_IFMT, dev);
if (ret < 0) {
ret = -errno;
- fprintf(stderr, "ERROR: mknod %s failed. %s\n", path,
- strerror(-ret));
+ error("mknod %s failed: %s", path, strerror(-ret));
}
out:
@@ -453,7 +446,7 @@ static int process_mkfifo(const char *path, void *user)
ret = path_cat_out(full_path, r->full_subvol_path, path);
if (ret < 0) {
- fprintf(stderr, "ERROR: mkfifo: path invalid: %s\n", path);
+ error("mkfifo: path invalid: %s", path);
goto out;
}
@@ -463,8 +456,7 @@ static int process_mkfifo(const char *path, void *user)
ret = mkfifo(full_path, 0600);
if (ret < 0) {
ret = -errno;
- fprintf(stderr, "ERROR: mkfifo %s failed. %s\n", path,
- strerror(-ret));
+ error("mkfifo %s failed: %s", path, strerror(-ret));
}
out:
@@ -479,7 +471,7 @@ static int process_mksock(const char *path, void *user)
ret = path_cat_out(full_path, r->full_subvol_path, path);
if (ret < 0) {
- fprintf(stderr, "ERROR: mksock: path invalid: %s\n", path);
+ error("mksock: path invalid: %s", path);
goto out;
}
@@ -489,8 +481,7 @@ static int process_mksock(const char *path, void *user)
ret = mknod(full_path, 0600 | S_IFSOCK, 0);
if (ret < 0) {
ret = -errno;
- fprintf(stderr, "ERROR: mknod %s failed. %s\n", path,
- strerror(-ret));
+ error("mknod %s failed: %s", path, strerror(-ret));
}
out:
@@ -505,7 +496,7 @@ static int process_symlink(const char *path, const char *lnk, void *user)
ret = path_cat_out(full_path, r->full_subvol_path, path);
if (ret < 0) {
- fprintf(stderr, "ERROR: symlink: path invalid: %s\n", path);
+ error("symlink: path invalid: %s", path);
goto out;
}
@@ -515,7 +506,7 @@ static int process_symlink(const char *path, const char *lnk, void *user)
ret = symlink(lnk, full_path);
if (ret < 0) {
ret = -errno;
- fprintf(stderr, "ERROR: symlink %s -> %s failed. %s\n", path,
+ error("symlink %s -> %s failed: %s", path,
lnk, strerror(-ret));
}
@@ -532,15 +523,13 @@ static int process_rename(const char *from, const char *to, void *user)
ret = path_cat_out(full_from, r->full_subvol_path, from);
if (ret < 0) {
- fprintf(stderr, "ERROR: rename: source path invalid: %s\n",
- from);
+ error("rename: source path invalid: %s", from);
goto out;
}
ret = path_cat_out(full_to, r->full_subvol_path, to);
if (ret < 0) {
- fprintf(stderr, "ERROR: rename: target path invalid: %s\n",
- to);
+ error("rename: target path invalid: %s", to);
goto out;
}
@@ -550,7 +539,7 @@ static int process_rename(const char *from, const char *to, void *user)
ret = rename(full_from, full_to);
if (ret < 0) {
ret = -errno;
- fprintf(stderr, "ERROR: rename %s -> %s failed. %s\n", from,
+ error("rename %s -> %s failed: %s", from,
to, strerror(-ret));
}
@@ -567,15 +556,13 @@ static int process_link(const char *path, const char *lnk, void *user)
ret = path_cat_out(full_path, r->full_subvol_path, path);
if (ret < 0) {
- fprintf(stderr, "ERROR: link: source path invalid: %s\n",
- full_path);
+ error("link: source path invalid: %s", full_path);
goto out;
}
ret = path_cat_out(full_link_path, r->full_subvol_path, lnk);
if (ret < 0) {
- fprintf(stderr, "ERROR: link: target path invalid: %s\n",
- full_link_path);
+ error("link: target path invalid: %s", full_link_path);
goto out;
}
@@ -585,8 +572,7 @@ static int process_link(const char *path, const char *lnk, void *user)
ret = link(full_link_path, full_path);
if (ret < 0) {
ret = -errno;
- fprintf(stderr, "ERROR: link %s -> %s failed. %s\n", path,
- lnk, strerror(-ret));
+ error("link %s -> %s failed: %s", path, lnk, strerror(-ret));
}
out:
@@ -602,7 +588,7 @@ static int process_unlink(const char *path, void *user)
ret = path_cat_out(full_path, r->full_subvol_path, path);
if (ret < 0) {
- fprintf(stderr, "ERROR: unlink: path invalid: %s\n", path);
+ error("unlink: path invalid: %s", path);
goto out;
}
@@ -612,8 +598,7 @@ static int process_unlink(const char *path, void *user)
ret = unlink(full_path);
if (ret < 0) {
ret = -errno;
- fprintf(stderr, "ERROR: unlink %s failed. %s\n", path,
- strerror(-ret));
+ error("unlink %s failed. %s", path, strerror(-ret));
}
out:
@@ -628,7 +613,7 @@ static int process_rmdir(const char *path, void *user)
ret = path_cat_out(full_path, r->full_subvol_path, path);
if (ret < 0) {
- fprintf(stderr, "ERROR: rmdir: path invalid: %s\n", path);
+ error("rmdir: path invalid: %s", path);
goto out;
}
@@ -638,8 +623,7 @@ static int process_rmdir(const char *path, void *user)
ret = rmdir(full_path);
if (ret < 0) {
ret = -errno;
- fprintf(stderr, "ERROR: rmdir %s failed. %s\n", path,
- strerror(-ret));
+ error("rmdir %s failed: %s", path, strerror(-ret));
}
out:
@@ -660,8 +644,7 @@ static int open_inode_for_write(struct btrfs_receive *r, const char *path)
r->write_fd = open(path, O_RDWR);
if (r->write_fd < 0) {
ret = -errno;
- fprintf(stderr, "ERROR: open %s failed. %s\n", path,
- strerror(-ret));
+ error("cannont open %s: %s", path, strerror(-ret));
goto out;
}
strncpy_null(r->write_path, path);
@@ -691,7 +674,7 @@ static int process_write(const char *path, const void *data, u64 offset,
ret = path_cat_out(full_path, r->full_subvol_path, path);
if (ret < 0) {
- fprintf(stderr, "ERROR: write: path invalid: %s\n", path);
+ error("write: path invalid: %s", path);
goto out;
}
@@ -704,7 +687,7 @@ static int process_write(const char *path, const void *data, u64 offset,
offset + pos);
if (w < 0) {
ret = -errno;
- fprintf(stderr, "ERROR: writing to %s failed. %s\n",
+ error("writing to %s failed: %s\n",
path, strerror(-ret));
goto out;
}
@@ -731,8 +714,7 @@ static int process_clone(const char *path, u64 offset, u64 len,
ret = path_cat_out(full_path, r->full_subvol_path, path);
if (ret < 0) {
- fprintf(stderr, "ERROR: clone: source path invalid: %s\n",
- path);
+ error("clone: source path invalid: %s", path);
goto out;
}
@@ -749,7 +731,7 @@ static int process_clone(const char *path, u64 offset, u64 len,
subvol_path = strdup(r->cur_subvol_path);
} else {
ret = -ENOENT;
- fprintf(stderr, "ERROR: did not find source subvol.\n");
+ error("clone: did not find source subvol");
goto out;
}
} else {
@@ -773,16 +755,14 @@ static int process_clone(const char *path, u64 offset, u64 len,
ret = path_cat_out(full_clone_path, subvol_path, clone_path);
if (ret < 0) {
- fprintf(stderr, "ERROR: clone: target path invalid: %s\n",
- clone_path);
+ error("clone: target path invalid: %s", clone_path);
goto out;
}
clone_fd = openat(r->mnt_fd, full_clone_path, O_RDONLY | O_NOATIME);
if (clone_fd < 0) {
ret = -errno;
- fprintf(stderr, "ERROR: failed to open %s. %s\n",
- full_clone_path, strerror(-ret));
+ error("cannot open %s: %s", full_clone_path, strerror(-ret));
goto out;
}
@@ -791,9 +771,9 @@ static int process_clone(const char *path, u64 offset, u64 len,
clone_args.src_length = len;
clone_args.dest_offset = offset;
ret = ioctl(r->write_fd, BTRFS_IOC_CLONE_RANGE, &clone_args);
- if (ret) {
+ if (ret < 0) {
ret = -errno;
- fprintf(stderr, "ERROR: failed to clone extents to %s\n%s\n",
+ error("failed to clone extents to %s\n%s\n",
path, strerror(-ret));
goto out;
}
@@ -819,7 +799,7 @@ static int process_set_xattr(const char *path, const char *name,
ret = path_cat_out(full_path, r->full_subvol_path, path);
if (ret < 0) {
- fprintf(stderr, "ERROR: set_xattr: path invalid: %s\n", path);
+ error("set_xattr: path invalid: %s", path);
goto out;
}
@@ -827,12 +807,10 @@ static int process_set_xattr(const char *path, const char *name,
if (g_verbose >= 3)
fprintf(stderr, "set_xattr: cache capabilities\n");
if (r->cached_capabilities_len)
- fprintf(stderr,
- "WARNING: capabilities set multiple times per file: %s\n",
+ warning("capabilities set multiple times per file: %s",
full_path);
if (len > sizeof(r->cached_capabilities)) {
- fprintf(stderr,
- "ERROR: capabilities encoded to %d bytes, buffer too small\n",
+ error("capabilities encoded to %d bytes, buffer too small",
len);
ret = -E2BIG;
goto out;
@@ -850,7 +828,7 @@ static int process_set_xattr(const char *path, const char *name,
ret = lsetxattr(full_path, name, data, len, 0);
if (ret < 0) {
ret = -errno;
- fprintf(stderr, "ERROR: lsetxattr %s %s=%.*s failed. %s\n",
+ error("lsetxattr %s %s=%.*s failed: %s",
path, name, len, (char*)data, strerror(-ret));
goto out;
}
@@ -867,8 +845,7 @@ static int process_remove_xattr(const char *path, const char *name, void *user)
ret = path_cat_out(full_path, r->full_subvol_path, path);
if (ret < 0) {
- fprintf(stderr, "ERROR: remove_xattr: path invalid: %s\n",
- path);
+ error("remove_xattr: path invalid: %s", path);
goto out;
}
@@ -880,7 +857,7 @@ static int process_remove_xattr(const char *path, const char *name, void *user)
ret = lremovexattr(full_path, name);
if (ret < 0) {
ret = -errno;
- fprintf(stderr, "ERROR: lremovexattr %s %s failed. %s\n",
+ error("lremovexattr %s %s failed: %s",
path, name, strerror(-ret));
goto out;
}
@@ -897,7 +874,7 @@ static int process_truncate(const char *path, u64 size, void *user)
ret = path_cat_out(full_path, r->full_subvol_path, path);
if (ret < 0) {
- fprintf(stderr, "ERROR: truncate: path invalid: %s\n", path);
+ error("truncate: path invalid: %s", path);
goto out;
}
@@ -907,8 +884,7 @@ static int process_truncate(const char *path, u64 size, void *user)
ret = truncate(full_path, size);
if (ret < 0) {
ret = -errno;
- fprintf(stderr, "ERROR: truncate %s failed. %s\n",
- path, strerror(-ret));
+ error("truncate %s failed: %s", path, strerror(-ret));
goto out;
}
@@ -924,7 +900,7 @@ static int process_chmod(const char *path, u64 mode, void *user)
ret = path_cat_out(full_path, r->full_subvol_path, path);
if (ret < 0) {
- fprintf(stderr, "ERROR: chmod: path invalid: %s\n", path);
+ error("chmod: path invalid: %s", path);
goto out;
}
@@ -934,8 +910,7 @@ static int process_chmod(const char *path, u64 mode, void *user)
ret = chmod(full_path, mode);
if (ret < 0) {
ret = -errno;
- fprintf(stderr, "ERROR: chmod %s failed. %s\n",
- path, strerror(-ret));
+ error("chmod %s failed: %s", path, strerror(-ret));
goto out;
}
@@ -951,7 +926,7 @@ static int process_chown(const char *path, u64 uid, u64 gid, void *user)
ret = path_cat_out(full_path, r->full_subvol_path, path);
if (ret < 0) {
- fprintf(stderr, "ERROR: chown: path invalid: %s\n", path);
+ error("chown: path invalid: %s", path);
goto out;
}
@@ -962,8 +937,7 @@ static int process_chown(const char *path, u64 uid, u64 gid, void *user)
ret = lchown(full_path, uid, gid);
if (ret < 0) {
ret = -errno;
- fprintf(stderr, "ERROR: chown %s failed. %s\n",
- path, strerror(-ret));
+ error("chown %s failed: %s", path, strerror(-ret));
goto out;
}
@@ -978,7 +952,7 @@ static int process_chown(const char *path, u64 uid, u64 gid, void *user)
r->cached_capabilities_len = 0;
if (ret < 0) {
ret = -errno;
- fprintf(stderr, "ERROR: restoring capabilities %s: %s\n",
+ error("restoring capabilities %s: %s",
path, strerror(-ret));
goto out;
}
@@ -999,7 +973,7 @@ static int process_utimes(const char *path, struct timespec *at,
ret = path_cat_out(full_path, r->full_subvol_path, path);
if (ret < 0) {
- fprintf(stderr, "ERROR: utimes: path invalid: %s\n", path);
+ error("utimes: path invalid: %s", path);
goto out;
}
@@ -1011,7 +985,7 @@ static int process_utimes(const char *path, struct timespec *at,
ret = utimensat(AT_FDCWD, full_path, tv, AT_SYMLINK_NOFOLLOW);
if (ret < 0) {
ret = -errno;
- fprintf(stderr, "ERROR: utimes %s failed. %s\n",
+ error("utimes %s failed: %s",
path, strerror(-ret));
goto out;
}
@@ -1071,15 +1045,13 @@ static int do_receive(struct btrfs_receive *r, const char *tomnt,
dest_dir_full_path = realpath(tomnt, NULL);
if (!dest_dir_full_path) {
ret = -errno;
- fprintf(stderr, "ERROR: realpath(%s) failed. %s\n", tomnt,
- strerror(-ret));
+ error("realpath(%s) failed: %s", tomnt, strerror(-ret));
goto out;
}
r->dest_dir_fd = open(dest_dir_full_path, O_RDONLY | O_NOATIME);
if (r->dest_dir_fd < 0) {
ret = -errno;
- fprintf(stderr,
- "ERROR: failed to open destination directory %s. %s\n",
+ error("cannot open destination directory %s: %s",
dest_dir_full_path, strerror(-ret));
goto out;
}
@@ -1089,16 +1061,14 @@ static int do_receive(struct btrfs_receive *r, const char *tomnt,
} else {
ret = find_mount_root(dest_dir_full_path, &r->root_path);
if (ret < 0) {
- fprintf(stderr,
- "ERROR: failed to determine mount point for %s: %s\n",
+ error("failed to determine mount point for %s: %s",
dest_dir_full_path, strerror(-ret));
ret = -EINVAL;
goto out;
}
if (ret > 0) {
- fprintf(stderr,
- "ERROR: %s doesn't belong to btrfs mount point\n",
- dest_dir_full_path);
+ error("%s doesn't belong to btrfs mount point",
+ dest_dir_full_path);
ret = -EINVAL;
goto out;
}
@@ -1106,8 +1076,7 @@ static int do_receive(struct btrfs_receive *r, const char *tomnt,
r->mnt_fd = open(r->root_path, O_RDONLY | O_NOATIME);
if (r->mnt_fd < 0) {
ret = -errno;
- fprintf(stderr, "ERROR: failed to open %s. %s\n", r->root_path,
- strerror(-ret));
+ error("cannot open %s: %s", r->root_path, strerror(-ret));
goto out;
}
@@ -1118,7 +1087,7 @@ static int do_receive(struct btrfs_receive *r, const char *tomnt,
*/
ret = btrfs_list_get_path_rootid(r->mnt_fd, &subvol_id);
if (ret) {
- fprintf(stderr, "ERROR: couldn't resolve our subvolid %d\n",
+ error("cannot resolve our subvolid: %d",
ret);
goto out;
}
@@ -1127,7 +1096,7 @@ static int do_receive(struct btrfs_receive *r, const char *tomnt,
ret = btrfs_subvolid_resolve(r->mnt_fd, root_subvol_path,
PATH_MAX, subvol_id);
if (ret) {
- fprintf(stderr, "ERROR: couldn't resolve our subvol path\n");
+ error("cannot resolve our subvol path");
goto out;
}
@@ -1135,22 +1104,19 @@ static int do_receive(struct btrfs_receive *r, const char *tomnt,
* Ok we're inside of a subvol off of the root subvol, we need to
* actually set full_root_path.
*/
- if (strlen(root_subvol_path))
+ if (*root_subvol_path)
r->full_root_path = root_subvol_path;
if (r->dest_dir_chroot) {
if (chroot(dest_dir_full_path)) {
ret = -errno;
- fprintf(stderr,
- "ERROR: failed to chroot to %s, %s\n",
- dest_dir_full_path,
- strerror(-ret));
+ error("failed to chroot to %s: %s",
+ dest_dir_full_path, strerror(-ret));
goto out;
}
if (chdir("/")) {
ret = -errno;
- fprintf(stderr,
- "ERROR: failed to chdir to /, %s\n",
+ error("failed to chdir to / after chroot: %s",
strerror(-ret));
goto out;
}
@@ -1201,7 +1167,9 @@ out:
close(r->write_fd);
r->write_fd = -1;
}
- free(r->root_path);
+
+ if (r->root_path != realmnt)
+ free(r->root_path);
r->root_path = NULL;
r->dest_dir_path = NULL;
free(dest_dir_full_path);
@@ -1254,9 +1222,8 @@ int cmd_receive(int argc, char **argv)
break;
case 'f':
if (arg_copy_path(fromfile, optarg, sizeof(fromfile))) {
- fprintf(stderr,
- "ERROR: input file path too long (%zu)\n",
- strlen(optarg));
+ error("input file path too long (%zu)",
+ strlen(optarg));
ret = 1;
goto out;
}
@@ -1272,16 +1239,15 @@ int cmd_receive(int argc, char **argv)
break;
case 'm':
if (arg_copy_path(realmnt, optarg, sizeof(realmnt))) {
- fprintf(stderr,
- "ERROR: mount point path too long (%zu)\n",
- strlen(optarg));
+ error("mount point path too long (%zu)",
+ strlen(optarg));
ret = 1;
goto out;
}
break;
case '?':
default:
- fprintf(stderr, "ERROR: receive args invalid.\n");
+ error("receive args invalid");
return 1;
}
}
@@ -1294,12 +1260,14 @@ int cmd_receive(int argc, char **argv)
if (fromfile[0]) {
receive_fd = open(fromfile, O_RDONLY | O_NOATIME);
if (receive_fd < 0) {
- fprintf(stderr, "ERROR: failed to open %s\n", fromfile);
+ error("cannot open %s: %s", fromfile, strerror(errno));
goto out;
}
}
ret = do_receive(&r, tomnt, realmnt, receive_fd, max_errors);
+ if (receive_fd != fileno(stdin))
+ close(receive_fd);
out:
diff --git a/cmds-replace.c b/cmds-replace.c
index 9ba256d..6036e2f 100644
--- a/cmds-replace.c
+++ b/cmds-replace.c
@@ -166,7 +166,7 @@ static int cmd_replace_start(int argc, char **argv)
status_args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_STATUS;
status_args.result = BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT;
ret = ioctl(fdmnt, BTRFS_IOC_DEV_REPLACE, &status_args);
- if (ret) {
+ if (ret < 0) {
fprintf(stderr,
"ERROR: ioctl(DEV_REPLACE_STATUS) failed on \"%s\": %s",
path, strerror(errno));
@@ -179,25 +179,21 @@ static int cmd_replace_start(int argc, char **argv)
}
if (status_args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR) {
- fprintf(stderr,
- "ERROR: ioctl(DEV_REPLACE_STATUS) on \"%s\" returns error: %s\n",
+ error("ioctl(DEV_REPLACE_STATUS) on '%s' returns error: %s",
path, replace_dev_result2string(status_args.result));
goto leave_with_error;
}
if (status_args.status.replace_state ==
BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED) {
- fprintf(stderr,
- "ERROR: btrfs replace on \"%s\" already started!\n",
- path);
+ error("device replace on '%s' already started", path);
goto leave_with_error;
}
srcdev = argv[optind];
dstdev = canonicalize_path(argv[optind + 1]);
if (!dstdev) {
- fprintf(stderr,
- "ERROR: Could not canonicalize path '%s': %s\n",
+ error("cannot canonicalize path '%s': %s",
argv[optind + 1], strerror(errno));
goto leave_with_error;
}
@@ -210,13 +206,12 @@ static int cmd_replace_start(int argc, char **argv)
ret = get_fs_info(path, &fi_args, &di_args);
if (ret) {
- fprintf(stderr, "ERROR: getting dev info for devstats failed: "
- "%s\n", strerror(-ret));
+ error("failed to get device info: %s", strerror(-ret));
free(di_args);
goto leave_with_error;
}
if (!fi_args.num_devices) {
- fprintf(stderr, "ERROR: no devices found\n");
+ error("no devices found");
free(di_args);
goto leave_with_error;
}
@@ -227,7 +222,7 @@ static int cmd_replace_start(int argc, char **argv)
srcdev_size = di_args[i].total_bytes;
free(di_args);
if (i == fi_args.num_devices) {
- fprintf(stderr, "Error: '%s' is not a valid devid for filesystem '%s'\n",
+ error("'%s' is not a valid devid for filesystem '%s'",
srcdev, path);
goto leave_with_error;
}
@@ -237,7 +232,7 @@ static int cmd_replace_start(int argc, char **argv)
start_args.start.srcdevid = 0;
srcdev_size = get_partition_size(srcdev);
} else {
- fprintf(stderr, "ERROR: source device must be a block device or a devid\n");
+ error("source device must be a block device or a devid");
goto leave_with_error;
}
@@ -247,14 +242,14 @@ static int cmd_replace_start(int argc, char **argv)
dstdev_size = get_partition_size(dstdev);
if (srcdev_size > dstdev_size) {
- fprintf(stderr, "ERROR: target device smaller than source device (required %llu bytes)\n",
+ error("target device smaller than source device (required %llu bytes)",
srcdev_size);
goto leave_with_error;
}
fddstdev = open(dstdev, O_RDWR);
if (fddstdev < 0) {
- fprintf(stderr, "Unable to open %s\n", dstdev);
+ error("unable to open %s: %s", dstdev, strerror(errno));
goto leave_with_error;
}
strncpy((char *)start_args.start.tgtdev_name, dstdev,
@@ -272,8 +267,7 @@ static int cmd_replace_start(int argc, char **argv)
dev_replace_handle_sigint(fdmnt);
if (!do_not_background) {
if (daemon(0, 0) < 0) {
- fprintf(stderr, "ERROR, backgrounding failed: %s\n",
- strerror(errno));
+ error("backgrounding failed: %s", strerror(errno));
goto leave_with_error;
}
}
@@ -282,7 +276,7 @@ static int cmd_replace_start(int argc, char **argv)
start_args.result = BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT;
ret = ioctl(fdmnt, BTRFS_IOC_DEV_REPLACE, &start_args);
if (do_not_background) {
- if (ret) {
+ if (ret < 0) {
fprintf(stderr,
"ERROR: ioctl(DEV_REPLACE_START) failed on \"%s\": %s",
path, strerror(errno));
@@ -293,16 +287,14 @@ static int cmd_replace_start(int argc, char **argv)
fprintf(stderr, "\n");
if (errno == EOPNOTSUPP)
- fprintf(stderr,
- "WARNING: dev_replace does not yet handle RAID5/6\n");
+ warning("device replace of RAID5/6 not supported with this kernel");
goto leave_with_error;
}
if (start_args.result !=
BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR) {
- fprintf(stderr,
- "ERROR: ioctl(DEV_REPLACE_START) on \"%s\" returns error: %s\n",
+ error("ioctl(DEV_REPLACE_START) on '%s' returns error: %s",
path,
replace_dev_result2string(start_args.result));
goto leave_with_error;
@@ -380,7 +372,7 @@ static int print_replace_status(int fd, const char *path, int once)
args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_STATUS;
args.result = BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT;
ret = ioctl(fd, BTRFS_IOC_DEV_REPLACE, &args);
- if (ret) {
+ if (ret < 0) {
fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_STATUS) failed on \"%s\": %s",
path, strerror(errno));
if (args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT)
@@ -392,7 +384,7 @@ static int print_replace_status(int fd, const char *path, int once)
}
if (args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR) {
- fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_STATUS) on \"%s\" returns error: %s\n",
+ error("ioctl(DEV_REPLACE_STATUS) on '%s' returns error: %s",
path,
replace_dev_result2string(args.result));
return -1;
@@ -444,8 +436,7 @@ static int print_replace_status(int fd, const char *path, int once)
printf("Never started");
break;
default:
- fprintf(stderr,
- "ERROR: ioctl(DEV_REPLACE_STATUS) on \"%s\" got unknown status: %llu\n",
+ error("unknown status from ioctl DEV_REPLACE_STATUS on '%s': %llu\n",
path, status->replace_state);
return -EINVAL;
}
@@ -531,7 +522,7 @@ static int cmd_replace_cancel(int argc, char **argv)
ret = ioctl(fd, BTRFS_IOC_DEV_REPLACE, &args);
e = errno;
close_file_or_dir(fd, dirstream);
- if (ret) {
+ if (ret < 0) {
fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_CANCEL) failed on \"%s\": %s",
path, strerror(e));
if (args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT)
diff --git a/cmds-rescue.c b/cmds-rescue.c
index fb3227b..9895425 100644
--- a/cmds-rescue.c
+++ b/cmds-rescue.c
@@ -43,15 +43,6 @@ static const char * const cmd_rescue_chunk_recover_usage[] = {
NULL
};
-static const char * const cmd_rescue_super_recover_usage[] = {
- "btrfs rescue super-recover [options] <device>",
- "Recover bad superblocks from good copies",
- "",
- "-y Assume an answer of `yes' to all questions",
- "-v Verbose mode",
- NULL
-};
-
static int cmd_rescue_chunk_recover(int argc, char *argv[])
{
int ret = 0;
@@ -84,26 +75,34 @@ static int cmd_rescue_chunk_recover(int argc, char *argv[])
ret = check_mounted(file);
if (ret < 0) {
- fprintf(stderr, "Could not check mount status: %s\n",
- strerror(-ret));
+ error("could not check mount status: %s", strerror(-ret));
return 1;
} else if (ret) {
- fprintf(stderr, "the device is busy\n");
+ error("the device is busy");
return 1;
}
ret = btrfs_recover_chunk_tree(file, verbose, yes);
if (!ret) {
- fprintf(stdout, "Recover the chunk tree successfully.\n");
+ fprintf(stdout, "Chunk tree recovered successfully\n");
} else if (ret > 0) {
ret = 0;
- fprintf(stdout, "Abort to rebuild the on-disk chunk tree.\n");
+ fprintf(stdout, "Chunk tree recovery aborted\n");
} else {
- fprintf(stdout, "Fail to recover the chunk tree.\n");
+ fprintf(stdout, "Chunk tree recovery failed\n");
}
return ret;
}
+static const char * const cmd_rescue_super_recover_usage[] = {
+ "btrfs rescue super-recover [options] <device>",
+ "Recover bad superblocks from good copies",
+ "",
+ "-y Assume an answer of `yes' to all questions",
+ "-v Verbose mode",
+ NULL
+};
+
/*
* return codes:
* 0 : All superblocks are valid, no need to recover
@@ -141,11 +140,10 @@ static int cmd_rescue_super_recover(int argc, char **argv)
dname = argv[optind];
ret = check_mounted(dname);
if (ret < 0) {
- fprintf(stderr, "Could not check mount status: %s\n",
- strerror(-ret));
+ error("could not check mount status: %s", strerror(-ret));
return 1;
} else if (ret) {
- fprintf(stderr, "the device is busy\n");
+ error("the device is busy");
return 1;
}
ret = btrfs_recover_superblocks(dname, verbose, yes);
@@ -173,16 +171,16 @@ static int cmd_rescue_zero_log(int argc, char **argv)
devname = argv[optind];
ret = check_mounted(devname);
if (ret < 0) {
- fprintf(stderr, "Could not check mount status: %s\n", strerror(-ret));
+ error("could not check mount status: %s", strerror(-ret));
goto out;
} else if (ret) {
- fprintf(stderr, "%s is currently mounted. Aborting.\n", devname);
+ error("%s is currently mounted", devname);
ret = -EBUSY;
}
root = open_ctree(devname, 0, OPEN_CTREE_WRITES | OPEN_CTREE_PARTIAL);
if (!root) {
- fprintf(stderr, "Could not open ctree\n");
+ error("could not open ctree");
return 1;
}
diff --git a/cmds-restore.c b/cmds-restore.c
index a1445d4..dd0b242 100644
--- a/cmds-restore.c
+++ b/cmds-restore.c
@@ -539,13 +539,10 @@ static int set_file_xattrs(struct btrfs_root *root, u64 inode,
len);
data_len = len;
- if (fsetxattr(fd, name, data, data_len, 0)) {
- int err = errno;
-
+ if (fsetxattr(fd, name, data, data_len, 0))
fprintf(stderr,
"Error setting extended attribute %s on file %s: %s\n",
- name, file_name, strerror(err));
- }
+ name, file_name, strerror(errno));
len = sizeof(*di) + name_len + data_len;
cur += len;
diff --git a/cmds-send.c b/cmds-send.c
index 581b25e..478ace1 100644
--- a/cmds-send.c
+++ b/cmds-send.c
@@ -198,14 +198,12 @@ static int write_buf(int fd, const void *buf, int size)
ret = write(fd, (char*)buf + pos, size - pos);
if (ret < 0) {
ret = -errno;
- fprintf(stderr, "ERROR: failed to dump stream. %s\n",
- strerror(-ret));
+ error("failed to dump stream: %s", strerror(-ret));
goto out;
}
if (!ret) {
ret = -EIO;
- fprintf(stderr, "ERROR: failed to dump stream. %s\n",
- strerror(-ret));
+ error("failed to dump stream: %s", strerror(-ret));
goto out;
}
pos += ret;
@@ -227,8 +225,8 @@ static void *dump_thread(void *arg_)
readed = read(s->send_fd, buf, sizeof(buf));
if (readed < 0) {
ret = -errno;
- fprintf(stderr, "ERROR: failed to read stream from "
- "kernel. %s\n", strerror(-ret));
+ error("failed to read stream from kernel: %s\n",
+ strerror(-ret));
goto out;
}
if (!readed) {
@@ -262,15 +260,14 @@ static int do_send(struct btrfs_send *send, u64 parent_root_id,
subvol_fd = openat(send->mnt_fd, subvol, O_RDONLY | O_NOATIME);
if (subvol_fd < 0) {
ret = -errno;
- fprintf(stderr, "ERROR: open %s failed. %s\n", subvol,
- strerror(-ret));
+ error("cannot open %s: %s", subvol, strerror(-ret));
goto out;
}
ret = pipe(pipefd);
if (ret < 0) {
ret = -errno;
- fprintf(stderr, "ERROR: pipe failed. %s\n", strerror(-ret));
+ error("pipe failed: %s", strerror(-ret));
goto out;
}
@@ -283,8 +280,7 @@ static int do_send(struct btrfs_send *send, u64 parent_root_id,
send);
if (ret) {
ret = -ret;
- fprintf(stderr, "ERROR: thread setup failed: %s\n",
- strerror(-ret));
+ error("thread setup failed: %s", strerror(-ret));
goto out;
}
@@ -297,10 +293,9 @@ static int do_send(struct btrfs_send *send, u64 parent_root_id,
if (!is_last_subvol)
io_send.flags |= BTRFS_SEND_FLAG_OMIT_END_CMD;
ret = ioctl(subvol_fd, BTRFS_IOC_SEND, &io_send);
- if (ret) {
+ if (ret < 0) {
ret = -errno;
- fprintf(stderr, "ERROR: send ioctl failed with %d: %s\n", ret,
- strerror(-ret));
+ error("send ioctl failed with %d: %s", ret, strerror(-ret));
if (ret == -EINVAL && (!is_first_subvol || !is_last_subvol))
fprintf(stderr,
"Try upgrading your kernel or don't use -e.\n");
@@ -318,14 +313,13 @@ static int do_send(struct btrfs_send *send, u64 parent_root_id,
ret = pthread_join(t_read, &t_err);
if (ret) {
ret = -ret;
- fprintf(stderr, "ERROR: pthread_join failed: %s\n",
- strerror(-ret));
+ error("pthread_join failed: %s", strerror(-ret));
goto out;
}
if (t_err) {
ret = (long int)t_err;
- fprintf(stderr, "ERROR: failed to process send stream, ret=%ld "
- "(%s)\n", (long int)t_err, strerror(-ret));
+ error("failed to process send stream, ret=%ld (%s)",
+ (long int)t_err, strerror(-ret));
goto out;
}
@@ -361,16 +355,13 @@ static int init_root_path(struct btrfs_send *s, const char *subvol)
ret = find_mount_root(subvol, &s->root_path);
if (ret < 0) {
- fprintf(stderr,
- "ERROR: failed to determine mount point for %s: %s\n",
+ error("failed to determine mount point for %s: %s",
subvol, strerror(-ret));
ret = -EINVAL;
goto out;
}
if (ret > 0) {
- fprintf(stderr,
- "ERROR: %s doesn't belong to btrfs mount point\n",
- subvol);
+ error("%s doesn't belong to btrfs mount point", subvol);
ret = -EINVAL;
goto out;
}
@@ -378,15 +369,14 @@ static int init_root_path(struct btrfs_send *s, const char *subvol)
s->mnt_fd = open(s->root_path, O_RDONLY | O_NOATIME);
if (s->mnt_fd < 0) {
ret = -errno;
- fprintf(stderr, "ERROR: can't open '%s': %s\n", s->root_path,
- strerror(-ret));
+ error("cannot open '%s': %s", s->root_path, strerror(-ret));
goto out;
}
ret = subvol_uuid_search_init(s->mnt_fd, &s->sus);
if (ret < 0) {
- fprintf(stderr, "ERROR: failed to initialize subvol search. "
- "%s\n", strerror(-ret));
+ error("failed to initialize subvol search: %s",
+ strerror(-ret));
goto out;
}
@@ -404,16 +394,15 @@ static int is_subvol_ro(struct btrfs_send *s, char *subvol)
fd = openat(s->mnt_fd, subvol, O_RDONLY | O_NOATIME);
if (fd < 0) {
ret = -errno;
- fprintf(stderr, "ERROR: failed to open %s. %s\n",
- subvol, strerror(-ret));
+ error("cannot open %s: %s", subvol, strerror(-ret));
goto out;
}
ret = ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags);
if (ret < 0) {
ret = -errno;
- fprintf(stderr, "ERROR: failed to get flags for subvolume. "
- "%s\n", strerror(-ret));
+ error("failed to get flags for subvolume %s: %s",
+ subvol, strerror(-ret));
goto out;
}
@@ -469,8 +458,7 @@ int cmd_send(int argc, char **argv)
subvol = realpath(optarg, NULL);
if (!subvol) {
ret = -errno;
- fprintf(stderr, "ERROR: realpath %s failed. "
- "%s\n", optarg, strerror(-ret));
+ error("realpath %s failed: %s\n", optarg, strerror(-ret));
goto out;
}
@@ -481,8 +469,7 @@ int cmd_send(int argc, char **argv)
ret = get_root_id(&send, get_subvol_name(send.root_path, subvol),
&root_id);
if (ret < 0) {
- fprintf(stderr, "ERROR: could not resolve "
- "root_id for %s\n", subvol);
+ error("cannot resolve rootid for %s", subvol);
goto out;
}
@@ -491,15 +478,13 @@ int cmd_send(int argc, char **argv)
goto out;
if (!ret) {
ret = -EINVAL;
- fprintf(stderr,
- "ERROR: cloned subvol %s is not read-only.\n",
- subvol);
+ error("cloned subvolume %s is not read-only", subvol);
goto out;
}
ret = add_clone_source(&send, root_id);
if (ret < 0) {
- fprintf(stderr, "ERROR: not enough memory\n");
+ error("not enough memory");
goto out;
}
subvol_uuid_search_finit(&send.sus);
@@ -515,24 +500,21 @@ int cmd_send(int argc, char **argv)
break;
case 'f':
if (arg_copy_path(outname, optarg, sizeof(outname))) {
- fprintf(stderr,
- "ERROR: output file path too long (%zu)\n",
- strlen(optarg));
+ error("output file path too long (%zu)", strlen(optarg));
ret = 1;
goto out;
}
break;
case 'p':
if (snapshot_parent) {
- fprintf(stderr, "ERROR: you cannot have more than one parent (-p)\n");
+ error("you cannot have more than one parent (-p)");
ret = 1;
goto out;
}
snapshot_parent = realpath(optarg, NULL);
if (!snapshot_parent) {
ret = -errno;
- fprintf(stderr, "ERROR: realpath %s failed. "
- "%s\n", optarg, strerror(-ret));
+ error("realpath %s failed: %s", optarg, strerror(-ret));
goto out;
}
@@ -541,8 +523,7 @@ int cmd_send(int argc, char **argv)
goto out;
if (!ret) {
ret = -EINVAL;
- fprintf(stderr,
- "ERROR: parent %s is not read-only.\n",
+ error("parent subvolume %s is not read-only",
snapshot_parent);
goto out;
}
@@ -550,8 +531,7 @@ int cmd_send(int argc, char **argv)
full_send = 0;
break;
case 'i':
- fprintf(stderr,
- "ERROR: -i was removed, use -c instead\n");
+ error("option -i was removed, use -c instead");
ret = 1;
goto out;
case GETOPT_VAL_SEND_NO_DATA:
@@ -559,7 +539,7 @@ int cmd_send(int argc, char **argv)
break;
case '?':
default:
- fprintf(stderr, "ERROR: send args invalid.\n");
+ error("send arguments invalid");
ret = 1;
goto out;
}
@@ -572,16 +552,14 @@ int cmd_send(int argc, char **argv)
send.dump_fd = creat(outname, 0600);
if (send.dump_fd == -1) {
ret = -errno;
- fprintf(stderr, "ERROR: can't create '%s': %s\n",
- outname, strerror(-ret));
+ error("cannot create '%s': %s", outname, strerror(-ret));
goto out;
}
}
if (isatty(send.dump_fd)) {
- fprintf(stderr,
- "ERROR: not dumping send stream into a terminal, "
- "redirect it into a file\n");
+ error(
+ "not dumping send stream into a terminal, redirect it into a file");
ret = 1;
goto out;
}
@@ -592,7 +570,7 @@ int cmd_send(int argc, char **argv)
subvol = realpath(argv[optind], NULL);
if (!subvol) {
ret = -errno;
- fprintf(stderr, "ERROR: unable to resolve %s\n", argv[optind]);
+ error("unable to resolve %s", argv[optind]);
goto out;
}
@@ -605,14 +583,13 @@ int cmd_send(int argc, char **argv)
get_subvol_name(send.root_path, snapshot_parent),
&parent_root_id);
if (ret < 0) {
- fprintf(stderr, "ERROR: could not resolve root_id "
- "for %s\n", snapshot_parent);
+ error("could not resolve rootid for %s", snapshot_parent);
goto out;
}
ret = add_clone_source(&send, parent_root_id);
if (ret < 0) {
- fprintf(stderr, "ERROR: not enough memory\n");
+ error("not enough memory");
goto out;
}
}
@@ -622,28 +599,25 @@ int cmd_send(int argc, char **argv)
subvol = realpath(argv[i], NULL);
if (!subvol) {
ret = -errno;
- fprintf(stderr, "ERROR: unable to resolve %s\n", argv[i]);
+ error("unable to resolve %s", argv[i]);
goto out;
}
ret = find_mount_root(subvol, &mount_root);
if (ret < 0) {
- fprintf(stderr, "ERROR: find_mount_root failed on %s: "
- "%s\n", subvol,
+ error("find_mount_root failed on %s: %s", subvol,
strerror(-ret));
goto out;
}
if (ret > 0) {
- fprintf(stderr,
- "ERROR: %s doesn't belong to btrfs mount point\n",
+ error("%s does not belong to btrfs mount point",
subvol);
ret = -EINVAL;
goto out;
}
if (strcmp(send.root_path, mount_root) != 0) {
ret = -EINVAL;
- fprintf(stderr, "ERROR: all subvols must be from the "
- "same fs.\n");
+ error("all subvolumes must be from the same filesystem");
goto out;
}
free(mount_root);
@@ -653,8 +627,7 @@ int cmd_send(int argc, char **argv)
goto out;
if (!ret) {
ret = -EINVAL;
- fprintf(stderr, "ERROR: %s is not read-only.\n",
- subvol);
+ error("subvolum %s is not read-only", subvol);
goto out;
}
}
@@ -674,15 +647,14 @@ int cmd_send(int argc, char **argv)
subvol = realpath(subvol, NULL);
if (!subvol) {
ret = -errno;
- fprintf(stderr, "ERROR: realpath %s failed. "
- "%s\n", argv[i], strerror(-ret));
+ error("realpath %s failed: %s", argv[i], strerror(-ret));
goto out;
}
if (!full_send && !parent_root_id) {
ret = find_good_parent(&send, root_id, &parent_root_id);
if (ret < 0) {
- fprintf(stderr, "ERROR: parent determination failed for %lld\n",
+ error("parent determination failed for %lld",
root_id);
goto out;
}
@@ -693,8 +665,7 @@ int cmd_send(int argc, char **argv)
goto out;
if (!ret) {
ret = -EINVAL;
- fprintf(stderr, "ERROR: %s is not read-only.\n",
- subvol);
+ error("subvolume %s is not read-only", subvol);
goto out;
}
@@ -715,7 +686,7 @@ int cmd_send(int argc, char **argv)
/* done with this subvol, so add it to the clone sources */
ret = add_clone_source(&send, root_id);
if (ret < 0) {
- fprintf(stderr, "ERROR: not enough memory\n");
+ error("not enough memory");
goto out;
}
diff --git a/cmds-subvolume.c b/cmds-subvolume.c
index be1a54a..9d9b0af 100644
--- a/cmds-subvolume.c
+++ b/cmds-subvolume.c
@@ -21,10 +21,12 @@
#include <sys/ioctl.h>
#include <errno.h>
#include <sys/stat.h>
+#include <sys/vfs.h>
#include <libgen.h>
#include <limits.h>
#include <getopt.h>
#include <uuid/uuid.h>
+#include <linux/magic.h>
#include "kerncompat.h"
#include "ioctl.h"
@@ -77,9 +79,9 @@ static int wait_for_subvolume_cleaning(int fd, int count, u64 *ids,
continue;
ret = is_subvolume_cleaned(fd, ids[i]);
if (ret < 0) {
- fprintf(stderr,
- "ERROR: can't perform the search - %s\n",
- strerror(-ret));
+ error(
+ "cannot read status of dead subvolume %llu: %s",
+ (unsigned long long)ids[i], strerror(-ret));
return ret;
}
if (ret) {
@@ -158,8 +160,12 @@ static int cmd_subvol_create(int argc, char **argv)
retval = 1; /* failure */
res = test_isdir(dst);
+ if (res < 0 && res != -ENOENT) {
+ error("cannot access %s: %s", dst, strerror(-res));
+ goto out;
+ }
if (res >= 0) {
- fprintf(stderr, "ERROR: '%s' exists\n", dst);
+ error("target path already exists: %s", dst);
goto out;
}
@@ -169,15 +175,13 @@ static int cmd_subvol_create(int argc, char **argv)
dstdir = dirname(dupdir);
if (!test_issubvolname(newname)) {
- fprintf(stderr, "ERROR: incorrect subvolume name '%s'\n",
- newname);
+ error("invalid subvolume name: %s", newname);
goto out;
}
len = strlen(newname);
if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
- fprintf(stderr, "ERROR: subvolume name too long '%s'\n",
- newname);
+ error("subvolume name too long: %s", newname);
goto out;
}
@@ -206,8 +210,7 @@ static int cmd_subvol_create(int argc, char **argv)
}
if (res < 0) {
- fprintf(stderr, "ERROR: cannot create subvolume - %s\n",
- strerror(errno));
+ error("cannot create subvolume: %s", strerror(errno));
goto out;
}
@@ -222,22 +225,30 @@ out:
}
/*
- * test if path is a subvolume:
- * this function return
- * 0-> path exists but it is not a subvolume
- * 1-> path exists and it is a subvolume
- * -1 -> path is unaccessible
+ * Test if path is a subvolume
+ * Returns:
+ * 0 - path exists but it is not a subvolume
+ * 1 - path exists and it is a subvolume
+ * < 0 - error
*/
-int test_issubvolume(char *path)
+int test_issubvolume(const char *path)
{
struct stat st;
+ struct statfs stfs;
int res;
res = stat(path, &st);
- if(res < 0 )
- return -1;
+ if (res < 0)
+ return -errno;
- return (st.st_ino == 256) && S_ISDIR(st.st_mode);
+ if (st.st_ino != BTRFS_FIRST_FREE_OBJECTID || !S_ISDIR(st.st_mode))
+ return 0;
+
+ res = statfs(path, &stfs);
+ if (res < 0)
+ return -errno;
+
+ return (int)stfs.f_type == BTRFS_SUPER_MAGIC;
}
static int wait_for_commit(int fd)
@@ -267,7 +278,7 @@ static const char * const cmd_subvol_delete_usage[] = {
static int cmd_subvol_delete(int argc, char **argv)
{
- int res, e, ret = 0;
+ int res, ret = 0;
int cnt;
int fd = -1;
struct btrfs_ioctl_vol_args args;
@@ -323,12 +334,12 @@ again:
res = test_issubvolume(path);
if (res < 0) {
- fprintf(stderr, "ERROR: error accessing '%s'\n", path);
+ error("cannot access subvolume %s: %s", path, strerror(-res));
ret = 1;
goto out;
}
if (!res) {
- fprintf(stderr, "ERROR: '%s' is not a subvolume\n", path);
+ error("not a subvolume: %s", path);
ret = 1;
goto out;
}
@@ -336,7 +347,7 @@ again:
cpath = realpath(path, NULL);
if (!cpath) {
ret = errno;
- fprintf(stderr, "ERROR: finding real path for '%s': %s\n",
+ error("cannot find real path for '%s': %s",
path, strerror(errno));
goto out;
}
@@ -358,11 +369,9 @@ again:
memset(&args, 0, sizeof(args));
strncpy_null(args.name, vname);
res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args);
- e = errno;
-
if(res < 0 ){
- fprintf( stderr, "ERROR: cannot delete '%s/%s' - %s\n",
- dname, vname, strerror(e));
+ error("cannot delete '%s/%s': %s", dname, vname,
+ strerror(errno));
ret = 1;
goto out;
}
@@ -370,8 +379,7 @@ again:
if (commit_mode == 1) {
res = wait_for_commit(fd);
if (res < 0) {
- fprintf(stderr,
- "ERROR: unable to wait for commit after '%s': %s\n",
+ error("unable to wait for commit after '%s': %s",
path, strerror(errno));
ret = 1;
}
@@ -394,8 +402,7 @@ out:
if (commit_mode == 2 && fd != -1) {
res = wait_for_commit(fd);
if (res < 0) {
- fprintf(stderr,
- "ERROR: unable to do final sync: %s\n",
+ error("unable to do final sync after deletion: %s",
strerror(errno));
ret = 1;
}
@@ -564,13 +571,13 @@ static int cmd_subvol_list(int argc, char **argv)
fd = btrfs_open_dir(subvol, &dirstream, 1);
if (fd < 0) {
ret = -1;
- fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
+ error("can't access '%s'", subvol);
goto out;
}
ret = btrfs_list_get_path_rootid(fd, &top_id);
if (ret) {
- fprintf(stderr, "ERROR: can't get rootid for '%s'\n", subvol);
+ error("can't get rootid for '%s'", subvol);
goto out;
}
@@ -682,17 +689,21 @@ static int cmd_subvol_snapshot(int argc, char **argv)
retval = 1; /* failure */
res = test_issubvolume(subvol);
if (res < 0) {
- fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
+ error("cannot access subvolume %s: %s", subvol, strerror(-res));
goto out;
}
if (!res) {
- fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
+ error("not a subvolume: %s", subvol);
goto out;
}
res = test_isdir(dst);
+ if (res < 0 && res != -ENOENT) {
+ error("cannot access %s: %s", dst, strerror(-res));
+ goto out;
+ }
if (res == 0) {
- fprintf(stderr, "ERROR: '%s' exists and it is not a directory\n", dst);
+ error("'%s' exists and it is not a directory", dst);
goto out;
}
@@ -708,15 +719,13 @@ static int cmd_subvol_snapshot(int argc, char **argv)
}
if (!test_issubvolname(newname)) {
- fprintf(stderr, "ERROR: incorrect snapshot name '%s'\n",
- newname);
+ error("invalid snapshot name '%s'", newname);
goto out;
}
len = strlen(newname);
if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
- fprintf(stderr, "ERROR: snapshot name too long '%s'\n",
- newname);
+ error("snapshot name too long '%s'", newname);
goto out;
}
@@ -748,8 +757,7 @@ static int cmd_subvol_snapshot(int argc, char **argv)
res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
if (res < 0) {
- fprintf( stderr, "ERROR: cannot snapshot '%s' - %s\n",
- subvol, strerror(errno));
+ error("cannot snapshot '%s': %s", subvol, strerror(errno));
goto out;
}
@@ -790,14 +798,14 @@ static int cmd_subvol_get_default(int argc, char **argv)
ret = btrfs_list_get_default_subvolume(fd, &default_id);
if (ret) {
- fprintf(stderr, "ERROR: can't perform the search - %s\n",
+ error("failed to look up default subvolume: %s",
strerror(errno));
goto out;
}
ret = 1;
if (default_id == 0) {
- fprintf(stderr, "ERROR: 'default' dir item not found\n");
+ error("'default' dir item not found");
goto out;
}
@@ -858,7 +866,7 @@ static int cmd_subvol_set_default(int argc, char **argv)
e = errno;
close_file_or_dir(fd, dirstream);
if (ret < 0) {
- fprintf(stderr, "ERROR: unable to set a new default subvolume - %s\n",
+ error("unable to set a new default subvolume: %s",
strerror(e));
return 1;
}
@@ -887,11 +895,11 @@ static int cmd_subvol_find_new(int argc, char **argv)
ret = test_issubvolume(subvol);
if (ret < 0) {
- fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
+ error("cannot access subvolume %s: %s", subvol, strerror(-ret));
return 1;
}
if (!ret) {
- fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
+ error("not a subvolume: %s", subvol);
return 1;
}
@@ -901,7 +909,7 @@ static int cmd_subvol_find_new(int argc, char **argv)
ret = ioctl(fd, BTRFS_IOC_SYNC);
if (ret < 0) {
- fprintf(stderr, "ERROR: unable to fs-syncing '%s' - %s\n",
+ error("sync ioctl failed on '%s': %s",
subvol, strerror(errno));
close_file_or_dir(fd, dirstream);
return 1;
@@ -931,37 +939,38 @@ static int cmd_subvol_show(int argc, char **argv)
int ret = 1;
DIR *dirstream1 = NULL, *dirstream2 = NULL;
- if (check_argc_exact(argc, 2))
+ clean_args_no_options(argc, argv, cmd_subvol_show_usage);
+
+ if (check_argc_exact(argc - optind, 1))
usage(cmd_subvol_show_usage);
- fullpath = realpath(argv[1], NULL);
+ fullpath = realpath(argv[optind], NULL);
if (!fullpath) {
- fprintf(stderr, "ERROR: finding real path for '%s', %s\n",
- argv[1], strerror(errno));
+ error("cannot find real path for '%s': %s",
+ argv[optind], strerror(errno));
goto out;
}
ret = test_issubvolume(fullpath);
if (ret < 0) {
- fprintf(stderr, "ERROR: error accessing '%s'\n", fullpath);
+ error("cannot access subvolume %s: %s", fullpath,
+ strerror(-ret));
goto out;
}
if (!ret) {
- fprintf(stderr, "ERROR: '%s' is not a subvolume\n", fullpath);
+ error("not a subvolume: %s", fullpath);
ret = 1;
goto out;
}
ret = find_mount_root(fullpath, &mnt);
if (ret < 0) {
- fprintf(stderr, "ERROR: find_mount_root failed on '%s': "
- "%s\n", fullpath, strerror(-ret));
+ error("find_mount_root failed on '%s': %s",
+ fullpath, strerror(-ret));
goto out;
}
if (ret > 0) {
- fprintf(stderr,
- "ERROR: %s doesn't belong to btrfs mount point\n",
- fullpath);
+ error("%s doesn't belong to btrfs mount point", fullpath);
goto out;
}
ret = 1;
@@ -973,8 +982,7 @@ static int cmd_subvol_show(int argc, char **argv)
ret = btrfs_list_get_path_rootid(fd, &sv_id);
if (ret) {
- fprintf(stderr, "ERROR: can't get rootid for '%s'\n",
- fullpath);
+ error("can't get rootid for '%s'", fullpath);
goto out;
}
@@ -983,7 +991,7 @@ static int cmd_subvol_show(int argc, char **argv)
goto out;
if (sv_id == BTRFS_FS_TREE_OBJECTID) {
- printf("%s is btrfs root\n", fullpath);
+ printf("%s is toplevel subvolume\n", fullpath);
goto out;
}
@@ -992,8 +1000,7 @@ static int cmd_subvol_show(int argc, char **argv)
ret = btrfs_get_subvol(mntfd, &get_ri);
if (ret) {
- fprintf(stderr, "ERROR: can't find '%s'\n",
- svpath);
+ error("can't find '%s'", svpath);
goto out;
}
@@ -1239,8 +1246,7 @@ static int cmd_subvol_sync(int argc, char **argv)
case 's':
sleep_interval = atoi(argv[optind]);
if (sleep_interval < 1) {
- fprintf(stderr,
- "ERROR: invalid sleep interval %s\n",
+ error("invalid sleep interval %s",
argv[optind]);
ret = 1;
goto out;
@@ -1265,7 +1271,7 @@ static int cmd_subvol_sync(int argc, char **argv)
if (!id_count) {
id_count = enumerate_dead_subvols(fd, &ids);
if (id_count < 0) {
- fprintf(stderr, "ERROR: can't enumerate dead subvolumes: %s\n",
+ error("can't enumerate dead subvolumes: %s",
strerror(-id_count));
ret = 1;
goto out;
@@ -1277,7 +1283,7 @@ static int cmd_subvol_sync(int argc, char **argv)
} else {
ids = (u64*)malloc(id_count * sizeof(u64));
if (!ids) {
- fprintf(stderr, "ERROR: not enough memory\n");
+ error("not enough memory");
ret = 1;
goto out;
}
@@ -1290,17 +1296,13 @@ static int cmd_subvol_sync(int argc, char **argv)
errno = 0;
id = strtoull(arg, NULL, 10);
if (errno < 0) {
- fprintf(stderr,
- "ERROR: unrecognized subvolume id %s\n",
- arg);
+ error("unrecognized subvolume id %s", arg);
ret = 1;
goto out;
}
if (id < BTRFS_FIRST_FREE_OBJECTID
|| id > BTRFS_LAST_FREE_OBJECTID) {
- fprintf(stderr,
- "ERROR: subvolume id %s out of range\n",
- arg);
+ error("subvolume id %s out of range\n", arg);
ret = 1;
goto out;
}
diff --git a/commands.h b/commands.h
index d2bb093..2da093b 100644
--- a/commands.h
+++ b/commands.h
@@ -126,7 +126,7 @@ int cmd_debug_tree(int argc, char **argv);
int cmd_rescue(int argc, char **argv);
/* subvolume exported functions */
-int test_issubvolume(char *path);
+int test_issubvolume(const char *path);
/* send.c */
char *get_subvol_name(char *mnt, char *full_path);
diff --git a/configure b/configure
index 36d9b44..0de03bb 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.3.
+# Generated by GNU Autoconf 2.69 for btrfs-progs v4.4.
#
# 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.3'
-PACKAGE_STRING='btrfs-progs v4.3'
+PACKAGE_VERSION='v4.4'
+PACKAGE_STRING='btrfs-progs v4.4'
PACKAGE_BUGREPORT='linux-btrfs@vger.kernel.org'
PACKAGE_URL='http://btrfs.wiki.kernel.org'
@@ -622,7 +622,7 @@ ac_includes_default="\
#endif"
ac_unique_file="btrfs.c"
-ac_default_prefix=/usr
+ac_default_prefix=/usr/local
ac_subst_vars='LTLIBOBJS
LIBOBJS
LIBBTRFS_PATCHLEVEL
@@ -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.3 to adapt to many kinds of systems.
+\`configure' configures btrfs-progs v4.4 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.3:";;
+ short | recursive ) echo "Configuration of btrfs-progs v4.4:";;
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.3
+btrfs-progs configure v4.4
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.3, which was
+It was created by btrfs-progs $as_me v4.4, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
@@ -4730,26 +4730,25 @@ else
$as_echo "no, using $LN_S" >&6; }
fi
-# Extract the first word of "ar", so it can be a program name with args.
-set dummy ar; ac_word=$2
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}ar", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ar; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_path_AR+:} false; then :
+if ${ac_cv_prog_AR+:} false; then :
$as_echo_n "(cached) " >&6
else
- case $AR in
- [\\/]* | ?:[\\/]*)
- ac_cv_path_AR="$AR" # Let the user override the test with a path.
- ;;
- *)
- as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+ if test -n "$AR"; then
+ ac_cv_prog_AR="$AR" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
- ac_cv_path_AR="$as_dir/$ac_word$ac_exec_ext"
+ ac_cv_prog_AR="${ac_tool_prefix}ar"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
@@ -4757,10 +4756,9 @@ done
done
IFS=$as_save_IFS
- ;;
-esac
fi
-AR=$ac_cv_path_AR
+fi
+AR=$ac_cv_prog_AR
if test -n "$AR"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5
$as_echo "$AR" >&6; }
@@ -4770,6 +4768,60 @@ $as_echo "no" >&6; }
fi
+fi
+if test -z "$ac_cv_prog_AR"; then
+ ac_ct_AR=$AR
+ # Extract the first word of "ar", so it can be a program name with args.
+set dummy ar; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_AR+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_AR"; then
+ ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_AR="ar"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_AR=$ac_cv_prog_ac_ct_AR
+if test -n "$ac_ct_AR"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5
+$as_echo "$ac_ct_AR" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_AR" = x; then
+ AR=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ AR=$ac_ct_AR
+ fi
+else
+ AR="$ac_cv_prog_AR"
+fi
+
# Extract the first word of "rm", so it can be a program name with args.
set dummy rm; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
@@ -6375,7 +6427,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
-This file was extended by btrfs-progs $as_me v4.3, which was
+This file was extended by btrfs-progs $as_me v4.4, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -6438,7 +6490,7 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
-btrfs-progs config.status v4.3
+btrfs-progs config.status v4.4
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
diff --git a/configure.ac b/configure.ac
index c3a22d1..fc343ea 100644
--- a/configure.ac
+++ b/configure.ac
@@ -23,7 +23,7 @@ dnl the compiler (like AC_PROG_LIBTOOL) to avoid autoconf errors.
AC_USE_SYSTEM_EXTENSIONS
AC_CONFIG_SRCDIR([btrfs.c])
-AC_PREFIX_DEFAULT([/usr])
+AC_PREFIX_DEFAULT([/usr/local])
AC_PROG_CC
AC_CANONICAL_HOST
@@ -35,7 +35,7 @@ AC_SYS_LARGEFILE
AC_PROG_INSTALL
AC_PROG_LN_S
-AC_PATH_PROG([AR], [ar])
+AC_CHECK_TOOL([AR], [ar])
AC_PATH_PROG([RM], [rm], [rm])
AC_PATH_PROG([RMDIR], [rmdir], [rmdir])
diff --git a/ctree.c b/ctree.c
index 46153e3..04cc476 100644
--- a/ctree.c
+++ b/ctree.c
@@ -2243,6 +2243,7 @@ split:
buf = kmalloc(item_size, GFP_NOFS);
+ BUG_ON(!buf);
read_extent_buffer(leaf, buf, btrfs_item_ptr_offset(leaf,
path->slots[0]), item_size);
slot = path->slots[0] + 1;
diff --git a/ctree.h b/ctree.h
index c57f9ca..2e1e631 100644
--- a/ctree.h
+++ b/ctree.h
@@ -76,6 +76,9 @@ struct btrfs_free_space_ctl;
/* for storing items that use the BTRFS_UUID_KEY* */
#define BTRFS_UUID_TREE_OBJECTID 9ULL
+/* tracks free space in block groups. */
+#define BTRFS_FREE_SPACE_TREE_OBJECTID 10ULL
+
/* for storing balance parameters in the root tree */
#define BTRFS_BALANCE_OBJECTID -4ULL
@@ -453,6 +456,8 @@ struct btrfs_super_block {
* Compat flags that we support. If any incompat flags are set other than the
* ones specified below then we will fail to mount
*/
+#define BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE (1ULL << 0)
+
#define BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF (1ULL << 0)
#define BTRFS_FEATURE_INCOMPAT_DEFAULT_SUBVOL (1ULL << 1)
#define BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS (1ULL << 2)
@@ -476,9 +481,10 @@ struct btrfs_super_block {
#define BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA (1ULL << 8)
#define BTRFS_FEATURE_INCOMPAT_NO_HOLES (1ULL << 9)
-
#define BTRFS_FEATURE_COMPAT_SUPP 0ULL
+
#define BTRFS_FEATURE_COMPAT_RO_SUPP 0ULL
+
#define BTRFS_FEATURE_INCOMPAT_SUPP \
(BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF | \
BTRFS_FEATURE_INCOMPAT_DEFAULT_SUBVOL | \
@@ -898,6 +904,13 @@ struct btrfs_block_group_item {
__le64 flags;
} __attribute__ ((__packed__));
+struct btrfs_free_space_info {
+ __le32 extent_count;
+ __le32 flags;
+} __attribute__ ((__packed__));
+
+#define BTRFS_FREE_SPACE_USING_BITMAPS (1ULL << 0)
+
struct btrfs_qgroup_info_item {
__le64 generation;
__le64 referenced;
@@ -965,6 +978,7 @@ struct btrfs_fs_info {
struct btrfs_root *dev_root;
struct btrfs_root *csum_root;
struct btrfs_root *quota_root;
+ struct btrfs_root *free_space_root;
struct rb_root fs_root_tree;
@@ -1157,6 +1171,27 @@ struct btrfs_root {
*/
#define BTRFS_BLOCK_GROUP_ITEM_KEY 192
+/*
+ * Every block group is represented in the free space tree by a free space info
+ * item, which stores some accounting information. It is keyed on
+ * (block_group_start, FREE_SPACE_INFO, block_group_length).
+ */
+#define BTRFS_FREE_SPACE_INFO_KEY 198
+
+/*
+ * A free space extent tracks an extent of space that is free in a block group.
+ * It is keyed on (start, FREE_SPACE_EXTENT, length).
+ */
+#define BTRFS_FREE_SPACE_EXTENT_KEY 199
+
+/*
+ * When a block group becomes very fragmented, we convert it to use bitmaps
+ * instead of extents. A free space bitmap is keyed on
+ * (start, FREE_SPACE_BITMAP, length); the corresponding item is a bitmap with
+ * (length / sectorsize) bits.
+ */
+#define BTRFS_FREE_SPACE_BITMAP_KEY 200
+
#define BTRFS_DEV_EXTENT_KEY 204
#define BTRFS_DEV_ITEM_KEY 216
#define BTRFS_CHUNK_ITEM_KEY 228
@@ -1394,6 +1429,11 @@ BTRFS_SETGET_FUNCS(disk_block_group_flags,
BTRFS_SETGET_STACK_FUNCS(block_group_flags,
struct btrfs_block_group_item, flags, 64);
+/* struct btrfs_free_space_info */
+BTRFS_SETGET_FUNCS(free_space_extent_count, struct btrfs_free_space_info,
+ extent_count, 32);
+BTRFS_SETGET_FUNCS(free_space_flags, struct btrfs_free_space_info, flags, 32);
+
/* struct btrfs_inode_ref */
BTRFS_SETGET_FUNCS(inode_ref_name_len, struct btrfs_inode_ref, name_len, 16);
BTRFS_SETGET_STACK_FUNCS(stack_inode_ref_name_len, struct btrfs_inode_ref, name_len, 16);
@@ -2193,6 +2233,13 @@ static inline int btrfs_fs_incompat(struct btrfs_fs_info *fs_info, u64 flag)
return !!(btrfs_super_incompat_flags(disk_super) & flag);
}
+static inline int btrfs_fs_compat_ro(struct btrfs_fs_info *fs_info, u64 flag)
+{
+ struct btrfs_super_block *disk_super;
+ disk_super = fs_info->super_copy;
+ return !!(btrfs_super_compat_ro_flags(disk_super) & flag);
+}
+
/* helper function to cast into the data area of the leaf. */
#define btrfs_item_ptr(leaf, slot, type) \
((type *)(btrfs_leaf_data(leaf) + \
@@ -2282,6 +2329,12 @@ int btrfs_record_file_extent(struct btrfs_trans_handle *trans,
u64 num_bytes);
int btrfs_free_block_group(struct btrfs_trans_handle *trans,
struct btrfs_fs_info *fs_info, u64 bytenr, u64 len);
+void free_excluded_extents(struct btrfs_root *root,
+ struct btrfs_block_group_cache *cache);
+int exclude_super_stripes(struct btrfs_root *root,
+ struct btrfs_block_group_cache *cache);
+u64 add_new_free_space(struct btrfs_block_group_cache *block_group,
+ struct btrfs_fs_info *info, u64 start, u64 end);
/* ctree.c */
int btrfs_comp_cpu_keys(struct btrfs_key *k1, struct btrfs_key *k2);
int btrfs_del_ptr(struct btrfs_trans_handle *trans, struct btrfs_root *root,
diff --git a/debian/changelog b/debian/changelog
index b421613..ea7b0dd 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+btrfs-tools (4.4-1) unstable; urgency=medium
+
+ * New upstream release.
+
+ -- Dimitri John Ledkov <xnox@ubuntu.com> Tue, 19 Jan 2016 19:03:41 +0000
+
btrfs-tools (4.3-1) unstable; urgency=medium
* New upstream release.
diff --git a/disk-io.c b/disk-io.c
index 139bc80..bd0444b 100644
--- a/disk-io.c
+++ b/disk-io.c
@@ -40,8 +40,6 @@
#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)
{
@@ -126,14 +124,10 @@ void btrfs_csum_final(u32 crc, char *result)
static int __csum_tree_block_size(struct extent_buffer *buf, u16 csum_size,
int verify, int silent)
{
- char *result;
+ char result[BTRFS_CSUM_SIZE];
u32 len;
u32 crc = ~(u32)0;
- result = malloc(csum_size * sizeof(char));
- if (!result)
- return 1;
-
len = buf->len - BTRFS_CSUM_SIZE;
crc = crc32c(crc, buf->data + BTRFS_CSUM_SIZE, len);
btrfs_csum_final(crc, result);
@@ -145,13 +139,11 @@ static int __csum_tree_block_size(struct extent_buffer *buf, u16 csum_size,
(unsigned long long)buf->start,
*((u32 *)result),
*((u32*)(char *)buf->data));
- free(result);
return 1;
}
} else {
write_extent_buffer(buf, result, 0, csum_size);
}
- free(result);
return 0;
}
@@ -819,6 +811,7 @@ void btrfs_free_fs_info(struct btrfs_fs_info *fs_info)
free(fs_info->dev_root);
free(fs_info->csum_root);
free(fs_info->quota_root);
+ free(fs_info->free_space_root);
free(fs_info->super_copy);
free(fs_info->log_root_tree);
free(fs_info);
@@ -838,12 +831,13 @@ struct btrfs_fs_info *btrfs_new_fs_info(int writable, u64 sb_bytenr)
fs_info->dev_root = calloc(1, sizeof(struct btrfs_root));
fs_info->csum_root = calloc(1, sizeof(struct btrfs_root));
fs_info->quota_root = calloc(1, sizeof(struct btrfs_root));
+ fs_info->free_space_root = calloc(1, sizeof(struct btrfs_root));
fs_info->super_copy = calloc(1, BTRFS_SUPER_INFO_SIZE);
if (!fs_info->tree_root || !fs_info->extent_root ||
!fs_info->chunk_root || !fs_info->dev_root ||
!fs_info->csum_root || !fs_info->quota_root ||
- !fs_info->super_copy)
+ !fs_info->free_space_root || !fs_info->super_copy)
goto free_all;
extent_io_tree_init(&fs_info->extent_cache);
@@ -1024,6 +1018,16 @@ int btrfs_setup_all_roots(struct btrfs_fs_info *fs_info, u64 root_tree_bytenr,
if (ret == 0)
fs_info->quota_enabled = 1;
+ if (btrfs_fs_compat_ro(fs_info, BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE)) {
+ ret = find_and_setup_root(root, fs_info, BTRFS_FREE_SPACE_TREE_OBJECTID,
+ fs_info->free_space_root);
+ if (ret) {
+ printk("Couldn't read free space tree\n");
+ return -EIO;
+ }
+ fs_info->free_space_root->track_dirty = 1;
+ }
+
ret = find_and_setup_log_root(root, fs_info, sb);
if (ret) {
printk("Couldn't setup log root tree\n");
@@ -1049,6 +1053,8 @@ int btrfs_setup_all_roots(struct btrfs_fs_info *fs_info, u64 root_tree_bytenr,
void btrfs_release_all_roots(struct btrfs_fs_info *fs_info)
{
+ if (fs_info->free_space_root)
+ free_extent_buffer(fs_info->free_space_root->node);
if (fs_info->quota_root)
free_extent_buffer(fs_info->quota_root->node);
if (fs_info->csum_root)
diff --git a/extent-cache.c b/extent-cache.c
index d80aead..38bed8b 100644
--- a/extent-cache.c
+++ b/extent-cache.c
@@ -282,3 +282,60 @@ void free_extent_cache_tree(struct cache_tree *tree)
{
cache_tree_free_extents(tree, free_extent_cache);
}
+
+int add_merge_cache_extent(struct cache_tree *tree, u64 start, u64 size)
+{
+ struct cache_extent *cache;
+ struct cache_extent *next = NULL;
+ struct cache_extent *prev = NULL;
+ int next_merged = 0;
+ int prev_merged = 0;
+ int ret = 0;
+
+ if (cache_tree_empty(tree))
+ goto insert;
+
+ cache = search_cache_extent(tree, start);
+ if (!cache) {
+ /*
+ * Either the tree is completely empty, or the no range after
+ * start.
+ * Either way, the last cache_extent should be prev.
+ */
+ prev = last_cache_extent(tree);
+ } else if (start <= cache->start) {
+ next = cache;
+ prev = prev_cache_extent(cache);
+ } else {
+ prev = cache;
+ next = next_cache_extent(cache);
+ }
+
+ /*
+ * Ensure the range to be inserted won't cover with existings
+ * Or we will need extra loop to do merge
+ */
+ BUG_ON(next && start + size > next->start);
+ BUG_ON(prev && prev->start + prev->size > start);
+
+ if (next && start + size == next->start) {
+ next_merged = 1;
+ next->size = next->start + next->size - start;
+ next->start = start;
+ }
+ if (prev && prev->start + prev->size == start) {
+ prev_merged = 1;
+ if (next_merged) {
+ next->size = next->start + next->size - prev->start;
+ next->start = prev->start;
+ remove_cache_extent(tree, prev);
+ free(prev);
+ } else {
+ prev->size = start + size - prev->start;
+ }
+ }
+insert:
+ if (!prev_merged && !next_merged)
+ ret = add_cache_extent(tree, start, size);
+ return ret;
+}
diff --git a/extent-cache.h b/extent-cache.h
index cb1f77c..f031fbf 100644
--- a/extent-cache.h
+++ b/extent-cache.h
@@ -45,11 +45,31 @@ struct cache_extent *last_cache_extent(struct cache_tree *tree);
struct cache_extent *prev_cache_extent(struct cache_extent *pe);
struct cache_extent *next_cache_extent(struct cache_extent *pe);
+/*
+ * Find a cache_extent which covers start.
+ *
+ * If not found, return next cache_extent if possible.
+ */
struct cache_extent *search_cache_extent(struct cache_tree *tree, u64 start);
+
+/*
+ * Find a cahce_extent which restrictly covers start.
+ *
+ * If not found, return NULL.
+ */
struct cache_extent *lookup_cache_extent(struct cache_tree *tree,
u64 start, u64 size);
+/*
+ * Add an non-overlap extent into cache tree
+ *
+ * If [start, start+size) overlap with existing one, it will return -EEXIST.
+ */
int add_cache_extent(struct cache_tree *tree, u64 start, u64 size);
+
+/*
+ * Same with add_cache_extent, but with cache_extent strcut.
+ */
int insert_cache_extent(struct cache_tree *tree, struct cache_extent *pe);
void remove_cache_extent(struct cache_tree *tree, struct cache_extent *pe);
@@ -71,12 +91,31 @@ static void free_##name##_tree(struct cache_tree *tree) \
void free_extent_cache_tree(struct cache_tree *tree);
+/*
+ * Search a cache_extent with same objectid, and covers start.
+ *
+ * If not found, return next if possible.
+ */
struct cache_extent *search_cache_extent2(struct cache_tree *tree,
u64 objectid, u64 start);
+/*
+ * Search a cache_extent with same objectid, and covers the range
+ * [start, start + size)
+ *
+ * If not found, return next cache_extent if possible.
+ */
struct cache_extent *lookup_cache_extent2(struct cache_tree *tree,
u64 objectid, u64 start, u64 size);
int add_cache_extent2(struct cache_tree *tree,
u64 objectid, u64 start, u64 size);
int insert_cache_extent2(struct cache_tree *tree, struct cache_extent *pe);
+/*
+ * Insert a cache_extent range [start, start + size).
+ *
+ * This function may merge with existing cache_extent.
+ * NOTE: caller must ensure the inserted range won't cover with any existing
+ * range.
+ */
+int add_merge_cache_extent(struct cache_tree *tree, u64 start, u64 size);
#endif
diff --git a/extent-tree.c b/extent-tree.c
index e04d962..1650bdb 100644
--- a/extent-tree.c
+++ b/extent-tree.c
@@ -4001,3 +4001,121 @@ fail:
btrfs_release_path(&path);
return ret;
}
+
+
+static int add_excluded_extent(struct btrfs_root *root,
+ u64 start, u64 num_bytes)
+{
+ u64 end = start + num_bytes - 1;
+ set_extent_bits(&root->fs_info->pinned_extents,
+ start, end, EXTENT_UPTODATE, GFP_NOFS);
+ return 0;
+}
+
+void free_excluded_extents(struct btrfs_root *root,
+ struct btrfs_block_group_cache *cache)
+{
+ u64 start, end;
+
+ start = cache->key.objectid;
+ end = start + cache->key.offset - 1;
+
+ clear_extent_bits(&root->fs_info->pinned_extents,
+ start, end, EXTENT_UPTODATE, GFP_NOFS);
+}
+
+int exclude_super_stripes(struct btrfs_root *root,
+ struct btrfs_block_group_cache *cache)
+{
+ u64 bytenr;
+ u64 *logical;
+ int stripe_len;
+ int i, nr, ret;
+
+ if (cache->key.objectid < BTRFS_SUPER_INFO_OFFSET) {
+ stripe_len = BTRFS_SUPER_INFO_OFFSET - cache->key.objectid;
+ cache->bytes_super += stripe_len;
+ ret = add_excluded_extent(root, cache->key.objectid,
+ stripe_len);
+ if (ret)
+ return ret;
+ }
+
+ for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) {
+ bytenr = btrfs_sb_offset(i);
+ ret = btrfs_rmap_block(&root->fs_info->mapping_tree,
+ cache->key.objectid, bytenr,
+ 0, &logical, &nr, &stripe_len);
+ if (ret)
+ return ret;
+
+ while (nr--) {
+ u64 start, len;
+
+ if (logical[nr] > cache->key.objectid +
+ cache->key.offset)
+ continue;
+
+ if (logical[nr] + stripe_len <= cache->key.objectid)
+ continue;
+
+ start = logical[nr];
+ if (start < cache->key.objectid) {
+ start = cache->key.objectid;
+ len = (logical[nr] + stripe_len) - start;
+ } else {
+ len = min_t(u64, stripe_len,
+ cache->key.objectid +
+ cache->key.offset - start);
+ }
+
+ cache->bytes_super += len;
+ ret = add_excluded_extent(root, start, len);
+ if (ret) {
+ kfree(logical);
+ return ret;
+ }
+ }
+
+ kfree(logical);
+ }
+ return 0;
+}
+
+u64 add_new_free_space(struct btrfs_block_group_cache *block_group,
+ struct btrfs_fs_info *info, u64 start, u64 end)
+{
+ u64 extent_start, extent_end, size, total_added = 0;
+ int ret;
+
+ while (start < end) {
+ ret = find_first_extent_bit(&info->pinned_extents, start,
+ &extent_start, &extent_end,
+ EXTENT_DIRTY | EXTENT_UPTODATE);
+ if (ret)
+ break;
+
+ if (extent_start <= start) {
+ start = extent_end + 1;
+ } else if (extent_start > start && extent_start < end) {
+ size = extent_start - start;
+ total_added += size;
+ ret = btrfs_add_free_space(block_group->free_space_ctl,
+ start, size);
+ BUG_ON(ret); /* -ENOMEM or logic error */
+ start = extent_end + 1;
+ } else {
+ break;
+ }
+ }
+
+ if (start < end) {
+ size = end - start;
+ total_added += size;
+ ret = btrfs_add_free_space(block_group->free_space_ctl, start,
+ size);
+ BUG_ON(ret); /* -ENOMEM or logic error */
+ }
+
+ return total_added;
+}
diff --git a/extent_io.c b/extent_io.c
index 75496ce..88e9273 100644
--- a/extent_io.c
+++ b/extent_io.c
@@ -884,3 +884,9 @@ void memset_extent_buffer(struct extent_buffer *eb, char c,
{
memset(eb->data + start, c, len);
}
+
+int extent_buffer_test_bit(struct extent_buffer *eb, unsigned long start,
+ unsigned long nr)
+{
+ return test_bit(nr, (unsigned long *)(eb->data + start));
+}
diff --git a/extent_io.h b/extent_io.h
index 27c4b69..a9a7353 100644
--- a/extent_io.h
+++ b/extent_io.h
@@ -148,6 +148,8 @@ void memmove_extent_buffer(struct extent_buffer *dst, unsigned long dst_offset,
unsigned long src_offset, unsigned long len);
void memset_extent_buffer(struct extent_buffer *eb, char c,
unsigned long start, unsigned long len);
+int extent_buffer_test_bit(struct extent_buffer *eb, unsigned long start,
+ unsigned long nr);
int set_extent_buffer_dirty(struct extent_buffer *eb);
int clear_extent_buffer_dirty(struct extent_buffer *eb);
int read_data_from_disk(struct btrfs_fs_info *info, void *buf, u64 offset,
diff --git a/find-root.c b/find-root.c
index 55e7942..f0204c8 100644
--- a/find-root.c
+++ b/find-root.c
@@ -108,8 +108,8 @@ int btrfs_find_root_search(struct btrfs_root *chunk_root,
{
struct btrfs_fs_info *fs_info = chunk_root->fs_info;
struct extent_buffer *eb;
- u64 metadata_offset = 0;
- u64 metadata_size = 0;
+ u64 chunk_offset = 0;
+ u64 chunk_size = 0;
u64 offset = 0;
u32 leafsize = chunk_root->leafsize;
int suppress_errors = 0;
@@ -118,15 +118,21 @@ int btrfs_find_root_search(struct btrfs_root *chunk_root,
suppress_errors = fs_info->suppress_check_block_errors;
fs_info->suppress_check_block_errors = 1;
while (1) {
- ret = btrfs_next_metadata(&fs_info->mapping_tree,
- &metadata_offset, &metadata_size);
+ if (filter->objectid != BTRFS_CHUNK_TREE_OBJECTID)
+ ret = btrfs_next_bg_metadata(&fs_info->mapping_tree,
+ &chunk_offset,
+ &chunk_size);
+ else
+ ret = btrfs_next_bg_system(&fs_info->mapping_tree,
+ &chunk_offset,
+ &chunk_size);
if (ret) {
if (ret == -ENOENT)
ret = 0;
break;
}
- for (offset = metadata_offset;
- offset < metadata_offset + metadata_size;
+ for (offset = chunk_offset;
+ offset < chunk_offset + chunk_size;
offset += chunk_root->leafsize) {
eb = read_tree_block(chunk_root, offset, leafsize, 0);
if (!eb || IS_ERR(eb))
diff --git a/free-space-cache.c b/free-space-cache.c
index 19ab0c9..d10a5f5 100644
--- a/free-space-cache.c
+++ b/free-space-cache.c
@@ -802,8 +802,8 @@ void btrfs_remove_free_space_cache(struct btrfs_block_group_cache *block_group)
__btrfs_remove_free_space_cache(block_group->free_space_ctl);
}
-static int btrfs_add_free_space(struct btrfs_free_space_ctl *ctl, u64 offset,
- u64 bytes)
+int btrfs_add_free_space(struct btrfs_free_space_ctl *ctl, u64 offset,
+ u64 bytes)
{
struct btrfs_free_space *info;
int ret = 0;
diff --git a/free-space-cache.h b/free-space-cache.h
index 85411a1..9214077 100644
--- a/free-space-cache.h
+++ b/free-space-cache.h
@@ -57,4 +57,6 @@ int btrfs_init_free_space_ctl(struct btrfs_block_group_cache *block_group,
int sectorsize);
void unlink_free_space(struct btrfs_free_space_ctl *ctl,
struct btrfs_free_space *info);
+int btrfs_add_free_space(struct btrfs_free_space_ctl *ctl, u64 offset,
+ u64 bytes);
#endif
diff --git a/free-space-tree.c b/free-space-tree.c
new file mode 100644
index 0000000..3c7a246
--- /dev/null
+++ b/free-space-tree.c
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2015 Facebook. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#include "ctree.h"
+#include "disk-io.h"
+#include "free-space-cache.h"
+#include "free-space-tree.h"
+
+static struct btrfs_free_space_info *
+search_free_space_info(struct btrfs_trans_handle *trans,
+ struct btrfs_fs_info *fs_info,
+ struct btrfs_block_group_cache *block_group,
+ struct btrfs_path *path, int cow)
+{
+ struct btrfs_root *root = fs_info->free_space_root;
+ struct btrfs_key key;
+ int ret;
+
+ key.objectid = block_group->key.objectid;
+ key.type = BTRFS_FREE_SPACE_INFO_KEY;
+ key.offset = block_group->key.offset;
+
+ ret = btrfs_search_slot(trans, root, &key, path, 0, cow);
+ if (ret < 0)
+ return ERR_PTR(ret);
+ if (ret != 0)
+ return ERR_PTR(-ENOENT);
+
+ return btrfs_item_ptr(path->nodes[0], path->slots[0],
+ struct btrfs_free_space_info);
+}
+
+static int free_space_test_bit(struct btrfs_block_group_cache *block_group,
+ struct btrfs_path *path, u64 offset,
+ u64 sectorsize)
+{
+ struct extent_buffer *leaf;
+ struct btrfs_key key;
+ u64 found_start, found_end;
+ unsigned long ptr, i;
+
+ leaf = path->nodes[0];
+ btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
+ ASSERT(key.type == BTRFS_FREE_SPACE_BITMAP_KEY);
+
+ found_start = key.objectid;
+ found_end = key.objectid + key.offset;
+ ASSERT(offset >= found_start && offset < found_end);
+
+ ptr = btrfs_item_ptr_offset(leaf, path->slots[0]);
+ i = (offset - found_start) / sectorsize;
+ return !!extent_buffer_test_bit(leaf, ptr, i);
+}
+
+static int load_free_space_bitmaps(struct btrfs_fs_info *fs_info,
+ struct btrfs_block_group_cache *block_group,
+ struct btrfs_path *path,
+ u32 expected_extent_count,
+ int *errors)
+{
+ struct btrfs_root *root = fs_info->free_space_root;
+ struct btrfs_key key;
+ int prev_bit = 0, bit;
+ u64 extent_start = 0;
+ u64 start, end, offset;
+ u32 extent_count = 0;
+ int ret;
+
+ start = block_group->key.objectid;
+ end = block_group->key.objectid + block_group->key.offset;
+
+ while (1) {
+ ret = btrfs_next_item(root, path);
+ if (ret < 0)
+ goto out;
+ if (ret)
+ break;
+
+ btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
+
+ if (key.type == BTRFS_FREE_SPACE_INFO_KEY)
+ break;
+
+ if (key.type != BTRFS_FREE_SPACE_BITMAP_KEY) {
+ fprintf(stderr, "unexpected key of type %u\n", key.type);
+ (*errors)++;
+ break;
+ }
+ if (key.objectid >= end) {
+ fprintf(stderr,
+ "free space bitmap starts at %llu, beyond end of block group %llu-%llu\n",
+ key.objectid, start, end);
+ (*errors)++;
+ break;
+ }
+ if (key.objectid + key.offset > end) {
+ fprintf(stderr,
+ "free space bitmap ends at %llu, beyond end of block group %llu-%llu\n",
+ key.objectid, start, end);
+ (*errors)++;
+ break;
+ }
+
+ offset = key.objectid;
+ while (offset < key.objectid + key.offset) {
+ bit = free_space_test_bit(block_group, path, offset,
+ root->sectorsize);
+ if (prev_bit == 0 && bit == 1) {
+ extent_start = offset;
+ } else if (prev_bit == 1 && bit == 0) {
+ add_new_free_space(block_group, fs_info, extent_start, offset);
+ extent_count++;
+ }
+ prev_bit = bit;
+ offset += root->sectorsize;
+ }
+ }
+
+ if (prev_bit == 1) {
+ add_new_free_space(block_group, fs_info, extent_start, end);
+ extent_count++;
+ }
+
+ if (extent_count != expected_extent_count) {
+ fprintf(stderr, "free space info recorded %u extents, counted %u\n",
+ expected_extent_count, extent_count);
+ (*errors)++;
+ }
+
+ ret = 0;
+out:
+ return ret;
+}
+
+static int load_free_space_extents(struct btrfs_fs_info *fs_info,
+ struct btrfs_block_group_cache *block_group,
+ struct btrfs_path *path,
+ u32 expected_extent_count,
+ int *errors)
+{
+ struct btrfs_root *root = fs_info->free_space_root;
+ struct btrfs_key key, prev_key;
+ int have_prev = 0;
+ u64 start, end;
+ u32 extent_count = 0;
+ int ret;
+
+ start = block_group->key.objectid;
+ end = block_group->key.objectid + block_group->key.offset;
+
+ while (1) {
+ ret = btrfs_next_item(root, path);
+ if (ret < 0)
+ goto out;
+ if (ret)
+ break;
+
+ btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
+
+ if (key.type == BTRFS_FREE_SPACE_INFO_KEY)
+ break;
+
+ if (key.type != BTRFS_FREE_SPACE_EXTENT_KEY) {
+ fprintf(stderr, "unexpected key of type %u\n", key.type);
+ (*errors)++;
+ break;
+ }
+ if (key.objectid >= end) {
+ fprintf(stderr,
+ "free space extent starts at %llu, beyond end of block group %llu-%llu\n",
+ key.objectid, start, end);
+ (*errors)++;
+ break;
+ }
+ if (key.objectid + key.offset > end) {
+ fprintf(stderr,
+ "free space extent ends at %llu, beyond end of block group %llu-%llu\n",
+ key.objectid, start, end);
+ (*errors)++;
+ break;
+ }
+
+ if (have_prev) {
+ u64 cur_start = key.objectid;
+ u64 cur_end = cur_start + key.offset;
+ u64 prev_start = prev_key.objectid;
+ u64 prev_end = prev_start + prev_key.offset;
+
+ if (cur_start < prev_end) {
+ fprintf(stderr,
+ "free space extent %llu-%llu overlaps with previous %llu-%llu\n",
+ cur_start, cur_end,
+ prev_start, prev_end);
+ (*errors)++;
+ } else if (cur_start == prev_end) {
+ fprintf(stderr,
+ "free space extent %llu-%llu is unmerged with previous %llu-%llu\n",
+ cur_start, cur_end,
+ prev_start, prev_end);
+ (*errors)++;
+ }
+ }
+
+ add_new_free_space(block_group, fs_info, key.objectid, key.objectid + key.offset);
+ extent_count++;
+
+ prev_key = key;
+ have_prev = 1;
+ }
+
+ if (extent_count != expected_extent_count) {
+ fprintf(stderr, "free space info recorded %u extents, counted %u\n",
+ expected_extent_count, extent_count);
+ (*errors)++;
+ }
+
+ ret = 0;
+out:
+ return ret;
+}
+
+int load_free_space_tree(struct btrfs_fs_info *fs_info,
+ struct btrfs_block_group_cache *block_group)
+{
+ struct btrfs_free_space_info *info;
+ struct btrfs_path *path;
+ u32 extent_count, flags;
+ int errors = 0;
+ int ret;
+
+ path = btrfs_alloc_path();
+ if (!path)
+ return -ENOMEM;
+ path->reada = 1;
+
+ info = search_free_space_info(NULL, fs_info, block_group, path, 0);
+ if (IS_ERR(info)) {
+ ret = PTR_ERR(info);
+ goto out;
+ }
+ extent_count = btrfs_free_space_extent_count(path->nodes[0], info);
+ flags = btrfs_free_space_flags(path->nodes[0], info);
+
+ if (flags & BTRFS_FREE_SPACE_USING_BITMAPS) {
+ ret = load_free_space_bitmaps(fs_info, block_group, path,
+ extent_count, &errors);
+ } else {
+ ret = load_free_space_extents(fs_info, block_group, path,
+ extent_count, &errors);
+ }
+ if (ret)
+ goto out;
+
+ ret = 0;
+out:
+ btrfs_free_path(path);
+ return ret ? ret : errors;
+}
diff --git a/free-space-tree.h b/free-space-tree.h
new file mode 100644
index 0000000..7529a46
--- /dev/null
+++ b/free-space-tree.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2015 Facebook. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#ifndef __BTRFS_FREE_SPACE_TREE_H__
+#define __BTRFS_FREE_SPACE_TREE_H__
+
+int load_free_space_tree(struct btrfs_fs_info *fs_info,
+ struct btrfs_block_group_cache *block_group);
+
+#endif
diff --git a/help.c b/help.c
index 1afc09d..c8bb720 100644
--- a/help.c
+++ b/help.c
@@ -259,3 +259,13 @@ void help_command_group(const struct cmd_group *grp, int argc, char **argv)
usage_command_group(grp, full, 0);
}
+
+int prefixcmp(const char *str, const char *prefix)
+{
+ for (; ; str++, prefix++)
+ if (!*prefix)
+ return 0;
+ else if (*str != *prefix)
+ return (unsigned char)*prefix - (unsigned char)*str;
+}
+
diff --git a/ioctl.h b/ioctl.h
index dff015a..771da23 100644
--- a/ioctl.h
+++ b/ioctl.h
@@ -216,7 +216,20 @@ struct btrfs_ioctl_feature_flags {
*/
struct btrfs_balance_args {
__u64 profiles;
- __u64 usage;
+
+ /*
+ * usage filter
+ * BTRFS_BALANCE_ARGS_USAGE with a single value means '0..N'
+ * BTRFS_BALANCE_ARGS_USAGE_RANGE - range syntax, min..max
+ */
+ union {
+ __u64 usage;
+ struct {
+ __u32 usage_min;
+ __u32 usage_max;
+ };
+ };
+
__u64 devid;
__u64 pstart;
__u64 pend;
@@ -227,8 +240,21 @@ struct btrfs_balance_args {
__u64 flags;
- __u64 limit; /* limit number of processed chunks */
- __u64 unused[7];
+ /*
+ * BTRFS_BALANCE_ARGS_LIMIT with value 'limit'
+ * BTRFS_BALANCE_ARGS_LIMIT_RANGE - the extend version can use minimum
+ * and maximum
+ */
+ union {
+ __u64 limit; /* limit number of processed chunks */
+ struct {
+ __u32 limit_min;
+ __u32 limit_max;
+ };
+ };
+ __u32 stripes_min;
+ __u32 stripes_max;
+ __u64 unused[6];
} __attribute__ ((__packed__));
/* report balance progress to userspace */
diff --git a/kerncompat.h b/kerncompat.h
index 7c627ba..0f207b7 100644
--- a/kerncompat.h
+++ b/kerncompat.h
@@ -310,6 +310,14 @@ static inline long IS_ERR(const void *ptr)
#define __bitwise
#endif
+/* Alignment check */
+#define IS_ALIGNED(x, a) (((x) & ((typeof(x))(a) - 1)) == 0)
+
+static inline int is_power_of_2(unsigned long n)
+{
+ return (n != 0 && ((n & (n - 1)) == 0));
+}
+
typedef u16 __bitwise __le16;
typedef u16 __bitwise __be16;
typedef u32 __bitwise __le32;
diff --git a/mkfs.c b/mkfs.c
index 0ea39f3..ea58404 100644
--- a/mkfs.c
+++ b/mkfs.c
@@ -252,7 +252,6 @@ static int create_raid_groups(struct btrfs_trans_handle *trans,
u64 metadata_profile, int mixed,
struct mkfs_allocation *allocation)
{
- u64 num_devices = btrfs_super_num_devices(root->fs_info->super_copy);
int ret;
if (metadata_profile) {
@@ -271,7 +270,7 @@ static int create_raid_groups(struct btrfs_trans_handle *trans,
BUG_ON(ret);
}
- if (!mixed && num_devices > 1 && data_profile) {
+ if (!mixed && data_profile) {
ret = create_one_raid_group(trans, root,
BTRFS_BLOCK_GROUP_DATA |
data_profile, allocation);
@@ -592,15 +591,14 @@ static int add_symbolic_link(struct btrfs_trans_handle *trans,
u64 objectid, const char *path_name)
{
int ret;
- u64 sectorsize = root->sectorsize;
- char *buf = malloc(sectorsize);
+ char buf[PATH_MAX];
- ret = readlink(path_name, buf, sectorsize);
+ ret = readlink(path_name, buf, sizeof(buf));
if (ret <= 0) {
fprintf(stderr, "readlink failed for %s\n", path_name);
goto fail;
}
- if (ret >= sectorsize) {
+ if (ret >= sizeof(buf)) {
fprintf(stderr, "symlink too long for %s\n", path_name);
ret = -1;
goto fail;
@@ -610,7 +608,6 @@ static int add_symbolic_link(struct btrfs_trans_handle *trans,
ret = btrfs_insert_inline_extent(trans, root, objectid, 0,
buf, ret + 1);
fail:
- free(buf);
return ret;
}
@@ -648,6 +645,12 @@ static int add_file_items(struct btrfs_trans_handle *trans,
if (st->st_size <= BTRFS_MAX_INLINE_DATA_SIZE(root)) {
char *buffer = malloc(st->st_size);
+
+ if (!buffer) {
+ ret = -ENOMEM;
+ goto end;
+ }
+
ret_read = pread64(fd, buffer, st->st_size, bytes_read);
if (ret_read == -1) {
fprintf(stderr, "%s read failed\n", path_name);
@@ -780,6 +783,8 @@ static int traverse_directory(struct btrfs_trans_handle *trans,
/* Add list for source directory */
dir_entry = malloc(sizeof(struct directory_name_entry));
+ if (!dir_entry)
+ return -ENOMEM;
dir_entry->dir_name = dir_name;
dir_entry->path = realpath(dir_name, real_path);
if (!dir_entry->path) {
@@ -881,6 +886,10 @@ static int traverse_directory(struct btrfs_trans_handle *trans,
if (S_ISDIR(st.st_mode)) {
dir_entry = malloc(sizeof(struct directory_name_entry));
+ if (!dir_entry) {
+ ret = -ENOMEM;
+ goto fail;
+ }
dir_entry->dir_name = cur_file->d_name;
dir_entry->path = make_path(parent_dir_entry->path,
cur_file->d_name);
@@ -1020,16 +1029,15 @@ out:
* This ignores symlinks with unreadable targets and subdirs that can't
* be read. It's a best-effort to give a rough estimate of the size of
* a subdir. It doesn't guarantee that prepopulating btrfs from this
- * tree won't still run out of space.
- *
- * The rounding up to 4096 is questionable. Previous code used du -B 4096.
+ * tree won't still run out of space.
*/
static u64 global_total_size;
+static u64 fs_block_size;
static int ftw_add_entry_size(const char *fpath, const struct stat *st,
int type)
{
if (type == FTW_F || type == FTW_D)
- global_total_size += round_up(st->st_size, 4096);
+ global_total_size += round_up(st->st_size, fs_block_size);
return 0;
}
@@ -1049,6 +1057,7 @@ static u64 size_sourcedir(char *dir_name, u64 sectorsize,
allocated_meta_size / default_chunk_size;
global_total_size = 0;
+ fs_block_size = sectorsize;
ret = ftw(dir_name, ftw_add_entry_size, 10);
dir_size = global_total_size;
if (ret < 0) {
@@ -1077,32 +1086,29 @@ static u64 size_sourcedir(char *dir_name, u64 sectorsize,
return total_size;
}
-static int zero_output_file(int out_fd, u64 size, u32 sectorsize)
+static int zero_output_file(int out_fd, u64 size)
{
- int len = sectorsize;
- int loop_num = size / sectorsize;
+ int loop_num;
u64 location = 0;
- char *buf = malloc(len);
+ char buf[4096];
int ret = 0, i;
ssize_t written;
- if (!buf)
- return -ENOMEM;
- memset(buf, 0, len);
+ memset(buf, 0, 4096);
+ loop_num = size / 4096;
for (i = 0; i < loop_num; i++) {
- written = pwrite64(out_fd, buf, len, location);
- if (written != len)
+ written = pwrite64(out_fd, buf, 4096, location);
+ if (written != 4096)
ret = -EIO;
- location += sectorsize;
+ location += 4096;
}
- free(buf);
return ret;
}
static int is_ssd(const char *file)
{
blkid_probe probe;
- char wholedisk[32];
+ char wholedisk[PATH_MAX];
char sysfs_path[PATH_MAX];
dev_t devno;
int fd;
@@ -1594,7 +1600,7 @@ int main(int ac, char **av)
}
}
ret = test_num_disk_vs_raid(metadata_profile, data_profile,
- dev_cnt, mixed);
+ dev_cnt, mixed, ssd);
if (ret)
exit(1);
@@ -1633,7 +1639,7 @@ int main(int ac, char **av)
&num_of_meta_chunks, &size_of_data);
if(block_count < source_dir_size)
block_count = source_dir_size;
- ret = zero_output_file(fd, block_count, sectorsize);
+ ret = zero_output_file(fd, block_count);
if (ret) {
fprintf(stderr, "unable to zero the output file\n");
exit(1);
diff --git a/print-tree.c b/print-tree.c
index 4d4c3a2..6704ff6 100644
--- a/print-tree.c
+++ b/print-tree.c
@@ -619,6 +619,15 @@ static void print_key_type(u64 objectid, u8 type)
case BTRFS_BLOCK_GROUP_ITEM_KEY:
printf("BLOCK_GROUP_ITEM");
break;
+ case BTRFS_FREE_SPACE_INFO_KEY:
+ printf("FREE_SPACE_INFO");
+ break;
+ case BTRFS_FREE_SPACE_EXTENT_KEY:
+ printf("FREE_SPACE_EXTENT");
+ break;
+ case BTRFS_FREE_SPACE_BITMAP_KEY:
+ printf("FREE_SPACE_BITMAP");
+ break;
case BTRFS_CHUNK_ITEM_KEY:
printf("CHUNK_ITEM");
break;
@@ -737,6 +746,9 @@ static void print_objectid(u64 objectid, u8 type)
case BTRFS_UUID_TREE_OBJECTID:
printf("UUID_TREE");
break;
+ case BTRFS_FREE_SPACE_TREE_OBJECTID:
+ printf("FREE_SPACE_TREE");
+ break;
case BTRFS_MULTIPLE_OBJECTIDS:
printf("MULTIPLE");
break;
@@ -819,6 +831,7 @@ void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l)
struct btrfs_dev_extent *dev_extent;
struct btrfs_disk_key disk_key;
struct btrfs_block_group_item bg_item;
+ struct btrfs_free_space_info *free_info;
struct btrfs_dir_log_item *dlog;
struct btrfs_qgroup_info_item *qg_info;
struct btrfs_qgroup_limit_item *qg_limit;
@@ -956,6 +969,18 @@ void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l)
(unsigned long long)btrfs_block_group_chunk_objectid(&bg_item),
flags_str);
break;
+ case BTRFS_FREE_SPACE_INFO_KEY:
+ free_info = btrfs_item_ptr(l, i, struct btrfs_free_space_info);
+ printf("\t\tfree space info extent count %u flags %u\n",
+ (unsigned)btrfs_free_space_extent_count(l, free_info),
+ (unsigned)btrfs_free_space_flags(l, free_info));
+ break;
+ case BTRFS_FREE_SPACE_EXTENT_KEY:
+ printf("\t\tfree space extent\n");
+ break;
+ case BTRFS_FREE_SPACE_BITMAP_KEY:
+ printf("\t\tfree space bitmap\n");
+ break;
case BTRFS_CHUNK_ITEM_KEY:
print_chunk(l, btrfs_item_ptr(l, i, struct btrfs_chunk));
break;
diff --git a/props.c b/props.c
index c9e2bd4..5b74932 100644
--- a/props.c
+++ b/props.c
@@ -194,5 +194,5 @@ const struct prop_handler prop_handlers[] = {
prop_object_dev | prop_object_root, prop_label},
{"compression", "Set/get compression for a file or directory", 0,
prop_object_inode, prop_compression},
- {0, 0, 0, 0, 0}
+ {NULL, NULL, 0, 0, NULL}
};
diff --git a/qgroup-verify.c b/qgroup-verify.c
index f7a94bf..7e3afda 100644
--- a/qgroup-verify.c
+++ b/qgroup-verify.c
@@ -37,24 +37,31 @@ static unsigned long tot_extents_scanned = 0;
static void add_bytes(u64 root_objectid, u64 num_bytes, int exclusive);
+struct qgroup_info {
+ u64 referenced;
+ u64 referenced_compressed;
+ u64 exclusive;
+ u64 exclusive_compressed;
+};
+
struct qgroup_count {
- u64 qgroupid;
- int subvol_exists;
+ u64 qgroupid;
+ int subvol_exists;
- struct btrfs_disk_key key;
- struct btrfs_qgroup_info_item diskinfo;
+ struct btrfs_disk_key key;
+ struct qgroup_info diskinfo;
- struct btrfs_qgroup_info_item info;
+ struct qgroup_info info;
- struct rb_node rb_node;
+ struct rb_node rb_node;
};
-struct counts_tree {
+static struct counts_tree {
struct rb_root root;
unsigned int num_groups;
} counts = { .root = RB_ROOT };
-struct rb_root by_bytenr = RB_ROOT;
+static struct rb_root by_bytenr = RB_ROOT;
/*
* List of interior tree blocks. We walk this list after loading the
@@ -68,8 +75,8 @@ struct rb_root by_bytenr = RB_ROOT;
* exist further down the tree, the fact that our interior node has a
* ref means we need to account anything below it to all its roots.
*/
-struct ulist *tree_blocks = NULL; /* unode->val = bytenr, ->aux
- * = tree_block pointer */
+static struct ulist *tree_blocks = NULL; /* unode->val = bytenr, ->aux
+ * = tree_block pointer */
struct tree_block {
int level;
u64 num_bytes;
@@ -647,14 +654,13 @@ static struct qgroup_count *alloc_count(struct btrfs_disk_key *key,
struct btrfs_qgroup_info_item *disk)
{
struct qgroup_count *c = calloc(1, sizeof(*c));
- struct btrfs_qgroup_info_item *item;
+ struct qgroup_info *item;
if (c) {
c->qgroupid = btrfs_disk_key_offset(key);
c->key = *key;
item = &c->diskinfo;
- item->generation = btrfs_qgroup_info_generation(leaf, disk);
item->referenced = btrfs_qgroup_info_referenced(leaf, disk);
item->referenced_compressed =
btrfs_qgroup_info_referenced_compressed(leaf, disk);
@@ -673,7 +679,7 @@ static struct qgroup_count *alloc_count(struct btrfs_disk_key *key,
static void add_bytes(u64 root_objectid, u64 num_bytes, int exclusive)
{
struct qgroup_count *count = find_count(root_objectid);
- struct btrfs_qgroup_info_item *qg;
+ struct qgroup_info *qg;
BUG_ON(num_bytes < 4096); /* Random sanity check. */
@@ -1014,8 +1020,8 @@ static void print_fields_signed(long long bytes,
static void print_qgroup_difference(struct qgroup_count *count, int verbose)
{
int is_different;
- struct btrfs_qgroup_info_item *info = &count->info;
- struct btrfs_qgroup_info_item *disk = &count->diskinfo;
+ struct qgroup_info *info = &count->info;
+ struct qgroup_info *disk = &count->diskinfo;
long long excl_diff = info->exclusive - disk->exclusive;
long long ref_diff = info->referenced - disk->referenced;
diff --git a/qgroup.c b/qgroup.c
index 1fbfcb9..a672ac0 100644
--- a/qgroup.c
+++ b/qgroup.c
@@ -1050,7 +1050,6 @@ static int __qgroups_search(int fd, struct qgroup_lookup *qgroup_lookup)
struct btrfs_ioctl_search_header *sh;
unsigned long off = 0;
unsigned int i;
- int e;
struct btrfs_qgroup_info_item *info;
struct btrfs_qgroup_limit_item *limit;
struct btrfs_qgroup *bq;
@@ -1075,11 +1074,10 @@ static int __qgroups_search(int fd, struct qgroup_lookup *qgroup_lookup)
while (1) {
ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
- e = errno;
if (ret < 0) {
fprintf(stderr,
"ERROR: can't perform the search - %s\n",
- strerror(e));
+ strerror(errno));
return ret;
}
/* the ioctl returns the number of item it found in nr_items */
@@ -1117,7 +1115,8 @@ static int __qgroups_search(int fd, struct qgroup_lookup *qgroup_lookup)
btrfs_stack_qgroup_info_exclusive_compressed
(info);
add_qgroup(qgroup_lookup, sh->offset, a1, a2,
- a3, a4, a5, 0, 0, 0, 0, 0, 0, 0);
+ a3, a4, a5, 0, 0, 0, 0, 0,
+ NULL, NULL);
} else if (sh->type == BTRFS_QGROUP_LIMIT_KEY) {
limit = (struct btrfs_qgroup_limit_item *)
(args.buf + off);
@@ -1132,7 +1131,8 @@ static int __qgroups_search(int fd, struct qgroup_lookup *qgroup_lookup)
a5 = btrfs_stack_qgroup_limit_rsv_exclusive
(limit);
add_qgroup(qgroup_lookup, sh->offset, 0, 0,
- 0, 0, 0, a1, a2, a3, a4, a5, 0, 0);
+ 0, 0, 0, a1, a2, a3, a4, a5,
+ NULL, NULL);
} else if (sh->type == BTRFS_QGROUP_RELATION_KEY) {
if (sh->offset < sh->objectid)
goto skip;
diff --git a/send-test.c b/send-test.c
index af8229e..4645b89 100644
--- a/send-test.c
+++ b/send-test.c
@@ -420,7 +420,7 @@ int main(int argc, char **argv)
io_send.flags = BTRFS_SEND_FLAG_NO_FILE_DATA;
ret = ioctl(subvol_fd, BTRFS_IOC_SEND, &io_send);
- if (ret) {
+ if (ret < 0) {
ret = errno;
fprintf(stderr, "ERROR: send ioctl failed with %d: %s\n", ret,
strerror(ret));
diff --git a/send-utils.c b/send-utils.c
index 51f3d7b..3c369b8 100644
--- a/send-utils.c
+++ b/send-utils.c
@@ -265,7 +265,7 @@ static int btrfs_subvolid_resolve_sub(int fd, char *path, size_t *path_len,
search_arg.key.max_transid = (u64)-1;
search_arg.key.nr_items = 1;
ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &search_arg);
- if (ret) {
+ if (ret < 0) {
fprintf(stderr,
"ioctl(BTRFS_IOC_TREE_SEARCH, subvol_id %llu) ret=%d, error: %s\n",
(unsigned long long)subvol_id, ret, strerror(errno));
@@ -302,7 +302,7 @@ static int btrfs_subvolid_resolve_sub(int fd, char *path, size_t *path_len,
ino_lookup_arg.objectid =
btrfs_stack_root_ref_dirid(backref_item);
ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &ino_lookup_arg);
- if (ret) {
+ if (ret < 0) {
fprintf(stderr,
"ioctl(BTRFS_IOC_INO_LOOKUP) ret=%d, error: %s\n",
ret, strerror(errno));
@@ -545,7 +545,6 @@ int subvol_uuid_search_init(int mnt_fd, struct subvol_uuid_search *s)
int root_item_valid = 0;
unsigned long off = 0;
int i;
- int e;
char *path;
s->mnt_fd = mnt_fd;
@@ -579,10 +578,9 @@ int subvol_uuid_search_init(int mnt_fd, struct subvol_uuid_search *s)
while (1) {
ret = ioctl(mnt_fd, BTRFS_IOC_TREE_SEARCH, &args);
- e = errno;
if (ret < 0) {
fprintf(stderr, "ERROR: can't perform the search - %s\n",
- strerror(e));
+ strerror(errno));
return ret;
}
if (sk->nr_items == 0)
diff --git a/string-table.c b/string-table.c
index 7e01412..5eda1ac 100644
--- a/string-table.c
+++ b/string-table.c
@@ -69,6 +69,7 @@ char *table_vprintf(struct string_table *tab, int column, int row,
* This function is like a printf, but store the results in a cell of
* the table.
*/
+__attribute__ ((format (printf, 4, 5)))
char *table_printf(struct string_table *tab, int column, int row,
char *fmt, ...)
{
@@ -101,7 +102,7 @@ void table_dump(struct string_table *tab)
continue;
len = strlen(tab->cells[idx]) - 1;
- if (len < 1 || tab->cells[idx][0] == '*')
+ if (len == 0 || tab->cells[idx][0] == '*')
continue;
if (len > sizes[i])
diff --git a/task-utils.c b/task-utils.c
index a4017ff..12b0002 100644
--- a/task-utils.c
+++ b/task-utils.c
@@ -119,6 +119,9 @@ void task_period_wait(struct task_info *info)
if (!info)
return;
+ if (info->periodic.timer_fd == 0)
+ return;
+
ret = read(info->periodic.timer_fd, &missed, sizeof (missed));
if (ret == -1)
return;
diff --git a/tests/convert-tests.sh b/tests/convert-tests.sh
index 4d99a61..b26c069 100755
--- a/tests/convert-tests.sh
+++ b/tests/convert-tests.sh
@@ -26,9 +26,9 @@ convert_test() {
shift
if [ -z "$features" ]; then
- echo " [TEST] $1, btrfs defaults"
+ echo " [TEST/conv] $1, btrfs defaults"
else
- echo " [TEST] $1, btrfs $features"
+ echo " [TEST/conv] $1, btrfs $features"
fi
nodesize=$2
shift 2
@@ -52,7 +52,7 @@ convert_test() {
}
if ! [ -z "$TEST" ]; then
- echo " [TEST] skipped all convert tests, TEST=$TEST"
+ echo " [TEST/conv] skipped all convert tests, TEST=$TEST"
exit 0
fi
diff --git a/tests/fsck-tests.sh b/tests/fsck-tests.sh
index b910e85..2aab4ff 100755
--- a/tests/fsck-tests.sh
+++ b/tests/fsck-tests.sh
@@ -32,7 +32,7 @@ run_one_test() {
local testname
testname="$1"
- echo " [TEST] $(basename $testname)"
+ echo " [TEST/fsck] $(basename $testname)"
cd $testname
echo "=== Entering $testname" >> $RESULTS
if [ -x test.sh ]; then
diff --git a/tests/fsck-tests/006-bad-root-items/test.sh b/tests/fsck-tests/006-bad-root-items/test.sh
index 421e225..8433234 100755
--- a/tests/fsck-tests/006-bad-root-items/test.sh
+++ b/tests/fsck-tests/006-bad-root-items/test.sh
@@ -2,6 +2,8 @@
source $TOP/tests/common
+check_prereq btrfs
+
echo "extracting image default_case.tar.xz" >> $RESULTS
tar --no-same-owner -xJf default_case.tar.xz || \
_fail "failed to extract default_case.tar.xz"
diff --git a/tests/fsck-tests/012-leaf-corruption/no_data_extent.tar.xz b/tests/fsck-tests/012-leaf-corruption/no_data_extent.tar.xz
index cc90b58..547e545 100644
--- a/tests/fsck-tests/012-leaf-corruption/no_data_extent.tar.xz
+++ b/tests/fsck-tests/012-leaf-corruption/no_data_extent.tar.xz
Binary files differ
diff --git a/tests/fsck-tests/012-leaf-corruption/test.sh b/tests/fsck-tests/012-leaf-corruption/test.sh
index 6e23145..a308727 100755
--- a/tests/fsck-tests/012-leaf-corruption/test.sh
+++ b/tests/fsck-tests/012-leaf-corruption/test.sh
@@ -2,6 +2,8 @@
source $TOP/tests/common
+check_prereq btrfs-image
+
# Check file list for leaf corruption, no regular/preallocated
# file extent case.
# Corrupted leaf is 20832256, which contains inode 1862~1872
@@ -29,6 +31,7 @@ leaf_no_data_ext_list=(
1869 0 40700 "snmp"
1871 0 100700 "machine-id"
1872 0 100700 "adjtime"
+ 1877 0 40700 "del"
)
generate_leaf_corrupt_no_data_ext()
@@ -40,10 +43,12 @@ generate_leaf_corrupt_no_data_ext()
$TOP/btrfs-image -r test.img.btrfs-image $dest || \
_fail "failed to extract leaf_corrupt_no_data_ext.btrfs-image"
- # leaf at 20832256 contains no regular data extent, clear its csum to
- # corrupt the leaf.
- dd if=/dev/zero of=$dest bs=1 count=32 conv=notrunc seek=20832256 \
- 1>/dev/null 2>&1
+ # leaf at 4206592 and 20905984 contains no regular data
+ # extent, clear its csum to corrupt the leaf.
+ for x in 4206592 20905984; do
+ dd if=/dev/zero of=$dest bs=1 count=32 conv=notrunc seek=$x \
+ 1>/dev/null 2>&1
+ done
}
check_inode()
diff --git a/tests/fsck-tests/013-extent-tree-rebuild/test.sh b/tests/fsck-tests/013-extent-tree-rebuild/test.sh
index 7419d6e..ff7d28e 100755
--- a/tests/fsck-tests/013-extent-tree-rebuild/test.sh
+++ b/tests/fsck-tests/013-extent-tree-rebuild/test.sh
@@ -2,8 +2,11 @@
source $TOP/tests/common
+check_prereq btrfs-corrupt-block
check_prereq btrfs-debug-tree
check_prereq mkfs.btrfs
+check_prereq btrfs
+
setup_root_helper
prepare_test_dev 1G
diff --git a/tests/fsck-tests/019-non-skinny-false-alert/default_case.img.xz b/tests/fsck-tests/019-non-skinny-false-alert/default_case.img.xz
new file mode 100644
index 0000000..c35f8bc
--- /dev/null
+++ b/tests/fsck-tests/019-non-skinny-false-alert/default_case.img.xz
Binary files differ
diff --git a/tests/fsck-tests/019-non-skinny-false-alert/test.sh b/tests/fsck-tests/019-non-skinny-false-alert/test.sh
new file mode 100755
index 0000000..a7f8e86
--- /dev/null
+++ b/tests/fsck-tests/019-non-skinny-false-alert/test.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# $ btrfs check img
+# Checking filesystem on img
+# UUID: 17f2bf15-f4c2-4ebc-b1f7-39b7af26257a
+# checking extents
+# bad extent [29376512, 29392896), type mismatch with chunk
+# bad extent [29442048, 29458432), type mismatch with chunk
+# bad extent [29589504, 29605888), type mismatch with chunk
+# ...
+#
+# a buggy check leads to the above messages
+
+source $TOP/tests/common
+
+check_prereq btrfs
+
+image=$(extract_image "./default_case.img.xz")
+run_check_stdout $TOP/btrfs check "$image" 2>&1 |
+ grep -q "type mismatch with chunk" &&
+ _fail "unexpected error message in the output"
+
+rm -f "$image"
diff --git a/tests/fuzz-tests.sh b/tests/fuzz-tests.sh
index 0e59832..204dce2 100755
--- a/tests/fuzz-tests.sh
+++ b/tests/fuzz-tests.sh
@@ -31,7 +31,7 @@ do
cd $i
if [ -x test.sh ]; then
echo "=== Entering $i" >> $RESULTS
- echo " [TEST] $name"
+ echo " [TEST/fuzz] $name"
./test.sh
if [ $? -ne 0 ]; then
_fail "test failed for case $(basename $i)"
diff --git a/tests/fuzz-tests/images/sys-array-num-stripes-0.raw.txt b/tests/fuzz-tests/images/sys-array-num-stripes-0.raw.txt
new file mode 100644
index 0000000..bdde4e7
--- /dev/null
+++ b/tests/fuzz-tests/images/sys-array-num-stripes-0.raw.txt
@@ -0,0 +1,30 @@
+URL: http://article.gmane.org/gmane.comp.file-systems.btrfs/50230
+Vegard Nossum, 2015-11-15
+
+If sys_array::num_stripes == 0, we hit a BUG_ON during mount:
+
+BTRFS: device fsid 9006933e-2a9a-44f0-917f-514252aeec2c devid 1 transid 7 /dev/loop0
+BTRFS info (device loop0): disk space caching is enabled
+BUG: failure at fs/btrfs/ctree.h:337/btrfs_chunk_item_size()!
+Kernel panic - not syncing: BUG!
+CPU: 0 PID: 313 Comm: mount Not tainted 4.2.5-00657-ge047887-dirty #25
+Stack:
+ 637af890 60062489 602aeb2e 604192ba
+ 60387961 00000011 637af8a0 6038a835
+ 637af9c0 6038776b 634ef32b 00000000
+Call Trace:
+ [<6001c86d>] show_stack+0xfe/0x15b
+ [<6038a835>] dump_stack+0x2a/0x2c
+ [<6038776b>] panic+0x13e/0x2b3
+ [<6020f099>] btrfs_read_sys_array+0x25d/0x2ff
+ [<601cfbbe>] open_ctree+0x192d/0x27af
+ [<6019c2c1>] btrfs_mount+0x8f5/0xb9a
+ [<600bc9a7>] mount_fs+0x11/0xf3
+ [<600d5167>] vfs_kern_mount+0x75/0x11a
+ [<6019bcb0>] btrfs_mount+0x2e4/0xb9a
+ [<600bc9a7>] mount_fs+0x11/0xf3
+ [<600d5167>] vfs_kern_mount+0x75/0x11a
+ [<600d710b>] do_mount+0xa35/0xbc9
+ [<600d7557>] SyS_mount+0x95/0xc8
+
+Fixed by patch (kernel and btrfs-progs): btrfs: handle invalid num_stripes in sys_array
diff --git a/tests/fuzz-tests/images/sys-array-num-stripes-0.raw.xz b/tests/fuzz-tests/images/sys-array-num-stripes-0.raw.xz
new file mode 100644
index 0000000..d64fb30
--- /dev/null
+++ b/tests/fuzz-tests/images/sys-array-num-stripes-0.raw.xz
Binary files differ
diff --git a/tests/misc-tests.sh b/tests/misc-tests.sh
index a87ece2..2a7f57c 100755
--- a/tests/misc-tests.sh
+++ b/tests/misc-tests.sh
@@ -34,7 +34,7 @@ check_prereq btrfs
for i in $(find $TOP/tests/misc-tests -maxdepth 1 -mindepth 1 -type d \
${TEST:+-name "$TEST"} | sort)
do
- echo " [TEST] $(basename $i)"
+ echo " [TEST/misc] $(basename $i)"
cd $i
echo "=== Entering $i" >> $RESULTS
if [ -x test.sh ]; then
diff --git a/tests/misc-tests/001-btrfstune-features/test.sh b/tests/misc-tests/001-btrfstune-features/test.sh
index 836e8d3..c858d70 100755
--- a/tests/misc-tests/001-btrfstune-features/test.sh
+++ b/tests/misc-tests/001-btrfstune-features/test.sh
@@ -6,6 +6,9 @@ source $TOP/tests/common
check_prereq btrfs-debug-tree
check_prereq btrfs-show-super
check_prereq mkfs.btrfs
+check_prereq btrfstune
+check_prereq btrfs
+
setup_root_helper
prepare_test_dev
diff --git a/tests/misc-tests/002-uuid-rewrite/test.sh b/tests/misc-tests/002-uuid-rewrite/test.sh
index 9b103aa..d84ec6c 100755
--- a/tests/misc-tests/002-uuid-rewrite/test.sh
+++ b/tests/misc-tests/002-uuid-rewrite/test.sh
@@ -7,6 +7,8 @@ check_prereq btrfs-debug-tree
check_prereq btrfs-show-super
check_prereq mkfs.btrfs
check_prereq btrfstune
+check_prereq btrfs
+
prepare_test_dev
get_fs_uuid() {
diff --git a/tests/misc-tests/004-shrink-fs/test.sh b/tests/misc-tests/004-shrink-fs/test.sh
index b132152..8874035 100755
--- a/tests/misc-tests/004-shrink-fs/test.sh
+++ b/tests/misc-tests/004-shrink-fs/test.sh
@@ -7,6 +7,8 @@
source $TOP/tests/common
check_prereq mkfs.btrfs
+check_prereq btrfs
+
setup_root_helper
# Optionally take id of the device to shrink
diff --git a/tests/misc-tests/005-convert-progress-thread-crash/test.sh b/tests/misc-tests/005-convert-progress-thread-crash/test.sh
index 09ac8a3..054069c 100755
--- a/tests/misc-tests/005-convert-progress-thread-crash/test.sh
+++ b/tests/misc-tests/005-convert-progress-thread-crash/test.sh
@@ -3,7 +3,8 @@
source $TOP/tests/common
-check_prereq btrfs
+check_prereq btrfs-convert
+
mkfs.ext4 -V &>/dev/null || _not_run "mkfs.ext4 not found"
prepare_test_dev 1G
diff --git a/tests/misc-tests/009-subvolume-sync-must-wait/test.sh b/tests/misc-tests/009-subvolume-sync-must-wait/test.sh
index 66d38ea..056584e 100755
--- a/tests/misc-tests/009-subvolume-sync-must-wait/test.sh
+++ b/tests/misc-tests/009-subvolume-sync-must-wait/test.sh
@@ -5,6 +5,8 @@
source $TOP/tests/common
check_prereq mkfs.btrfs
+check_prereq btrfs
+
setup_root_helper
run_check truncate -s 2G $IMAGE
diff --git a/tests/misc-tests/010-convert-delete-ext2-subvol/test.sh b/tests/misc-tests/010-convert-delete-ext2-subvol/test.sh
index 4893647..451e453 100755
--- a/tests/misc-tests/010-convert-delete-ext2-subvol/test.sh
+++ b/tests/misc-tests/010-convert-delete-ext2-subvol/test.sh
@@ -6,7 +6,9 @@
source $TOP/tests/common
check_prereq btrfs-convert
+check_prereq btrfs-debug-tree
check_prereq btrfs
+
setup_root_helper
prepare_test_dev
diff --git a/tests/misc-tests/011-delete-missing-device/test.sh b/tests/misc-tests/011-delete-missing-device/test.sh
new file mode 100755
index 0000000..26645f1
--- /dev/null
+++ b/tests/misc-tests/011-delete-missing-device/test.sh
@@ -0,0 +1,83 @@
+#!/bin/bash
+# make sure that 'missing' is accepted for device deletion
+
+source $TOP/tests/common
+
+check_prereq btrfs-show-super
+check_prereq mkfs.btrfs
+check_prereq btrfs
+
+ndevs=4
+declare -a devs
+dev1=
+devtodel=
+
+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_mayfail $SUDO_HELPER losetup -d $dev
+ done
+ for i in `seq $ndevs`; do
+ truncate -s0 img$i
+ done
+ run_check $SUDO_HELPER losetup --list
+}
+
+test_do_mkfs()
+{
+ run_check $SUDO_HELPER $TOP/mkfs.btrfs -f $@ ${devs[@]}
+ run_check $TOP/btrfs-show-super $dev1
+ run_check $SUDO_HELPER $TOP/btrfs check $dev1
+ run_check $TOP/btrfs filesystem show
+}
+
+test_wipefs()
+{
+ run_check wipefs -a $devtodel
+ run_check $SUDO_HELPER losetup -d $devtodel
+ run_check losetup -a
+ run_check $TOP/btrfs filesystem show
+}
+test_delete_missing()
+{
+ run_check_mount_test_dev -o degraded
+ run_check $SUDO_HELPER $TOP/btrfs filesystem show $TEST_MNT
+ run_check $SUDO_HELPER $TOP/btrfs device delete missing $TEST_MNT
+ run_check $SUDO_HELPER $TOP/btrfs filesystem show $TEST_MNT
+ run_check_umount_test_dev
+
+ run_check_mount_test_dev
+ local out
+ out="$(run_check_stdout $SUDO_HELPER $TOP/btrfs filesystem show $TEST_MNT)"
+ if echo "$out" | grep -q -- "$devtodel"; then
+ _fail "device $devtodel not deleted"
+ fi
+ if echo "$out" | grep -q missing; then
+ _fail "missing device still present"
+ fi
+ run_check_umount_test_dev
+}
+
+prepare_devices
+dev1=${devs[1]}
+devtodel=${devs[3]}
+TEST_DEV=$dev1
+
+test_do_mkfs
+test_wipefs
+test_delete_missing
+
+cleanup_devices
diff --git a/tests/mkfs-tests.sh b/tests/mkfs-tests.sh
index 4780b54..c0635ad 100755
--- a/tests/mkfs-tests.sh
+++ b/tests/mkfs-tests.sh
@@ -31,7 +31,7 @@ check_prereq btrfs
for i in $(find $TOP/tests/mkfs-tests -maxdepth 1 -mindepth 1 -type d \
${TEST:+-name "$TEST"} | sort)
do
- echo " [TEST] $(basename $i)"
+ echo " [TEST/mkfs] $(basename $i)"
cd $i
echo "=== Entering $i" >> $RESULTS
if [ -x test.sh ]; then
diff --git a/tests/mkfs-tests/001-basic-profiles/test.sh b/tests/mkfs-tests/001-basic-profiles/test.sh
index 0861f36..2747d42 100755
--- a/tests/mkfs-tests/001-basic-profiles/test.sh
+++ b/tests/mkfs-tests/001-basic-profiles/test.sh
@@ -70,6 +70,8 @@ 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 single
+test_mkfs_single -d dup -m dup
test_mkfs_single -d dup -m dup --mixed
test_mkfs_multi
diff --git a/tests/mkfs-tests/002-no-force-mixed-on-small-volume/test.sh b/tests/mkfs-tests/002-no-force-mixed-on-small-volume/test.sh
index 007a0eb..855fbd1 100755
--- a/tests/mkfs-tests/002-no-force-mixed-on-small-volume/test.sh
+++ b/tests/mkfs-tests/002-no-force-mixed-on-small-volume/test.sh
@@ -4,8 +4,8 @@
source $TOP/tests/common
-check_prereq btrfs-show-super
check_prereq mkfs.btrfs
+
setup_root_helper
run_check truncate -s 512M $IMAGE
diff --git a/tests/mkfs-tests/004-rootdir-keeps-size/test.sh b/tests/mkfs-tests/004-rootdir-keeps-size/test.sh
index a78a3de..7038c8e 100755
--- a/tests/mkfs-tests/004-rootdir-keeps-size/test.sh
+++ b/tests/mkfs-tests/004-rootdir-keeps-size/test.sh
@@ -4,6 +4,7 @@
source $TOP/tests/common
check_prereq mkfs.btrfs
+
prepare_test_dev
test_mkfs_with_size() {
diff --git a/tests/mkfs-tests/005-long-device-name-for-ssd/test.sh b/tests/mkfs-tests/005-long-device-name-for-ssd/test.sh
new file mode 100755
index 0000000..c89ee0e
--- /dev/null
+++ b/tests/mkfs-tests/005-long-device-name-for-ssd/test.sh
@@ -0,0 +1,40 @@
+#!/bin/bash
+# a long device name must pass the SSD test
+
+source $TOP/tests/common
+
+check_prereq mkfs.btrfs
+check_prereq btrfs-show-super
+
+setup_root_helper
+prepare_test_dev
+
+# prep device
+dmname=\
+btrfs-test-with-very-long-name-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+dmdev=/dev/mapper/$dmname
+
+run_check truncate -s0 img
+chmod a+w img
+run_check truncate -s2g img
+
+loopdev=`run_check_stdout $SUDO_HELPER losetup --find --show img`
+run_check $SUDO_HELPER dmsetup create $dmname --table "0 1048576 linear $loopdev 0"
+
+base=`basename "$loopdev"`
+rot=/sys/class/block/$base/queue/rotational
+
+# switch rotational
+run_check cat $rot
+echo 0 | run_check $SUDO_HELPER tee $rot
+run_check cat $rot
+
+# test
+run_check_stdout $SUDO_HELPER $TOP/mkfs.btrfs -f $@ $dmdev |
+ grep -q 'SSD detected:.*yes' || _fail 'SSD not detected'
+run_check $TOP/btrfs-show-super $dmdev
+
+# cleanup
+run_check $SUDO_HELPER dmsetup remove $dmname
+run_mayfail $SUDO_HELPER losetup -d $loopdev
+run_check truncate -s0 img
diff --git a/tests/mkfs-tests/006-partitioned-loopdev/partition-1g-1g b/tests/mkfs-tests/006-partitioned-loopdev/partition-1g-1g
new file mode 100644
index 0000000..eb05776
--- /dev/null
+++ b/tests/mkfs-tests/006-partitioned-loopdev/partition-1g-1g
Binary files differ
diff --git a/tests/mkfs-tests/006-partitioned-loopdev/test.sh b/tests/mkfs-tests/006-partitioned-loopdev/test.sh
new file mode 100755
index 0000000..7c9fb82
--- /dev/null
+++ b/tests/mkfs-tests/006-partitioned-loopdev/test.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+# recognize partitioned loop devices
+
+source $TOP/tests/common
+
+check_prereq mkfs.btrfs
+check_prereq btrfs-show-super
+
+setup_root_helper
+
+run_check truncate -s0 img
+chmod a+w img
+cp partition-1g-1g img
+run_check truncate -s2g img
+
+loopdev=$(run_check_stdout $SUDO_HELPER losetup --partscan --find --show img)
+base=$(basename $loopdev)
+
+# expect partitions named like loop0p1 etc
+for looppart in $(ls /dev/$base?*); do
+ run_check $SUDO_HELPER $TOP/mkfs.btrfs -f $looppart
+ run_check $TOP/btrfs-show-super $looppart
+done
+
+# cleanup
+run_check $SUDO_HELPER losetup -d $loopdev
+run_check truncate -s0 img
diff --git a/tests/mkfs-tests/007-mix-nodesize-sectorsize/test.sh b/tests/mkfs-tests/007-mix-nodesize-sectorsize/test.sh
new file mode 100755
index 0000000..d5374cb
--- /dev/null
+++ b/tests/mkfs-tests/007-mix-nodesize-sectorsize/test.sh
@@ -0,0 +1,36 @@
+#!/bin/bash
+# iterate over nodesize and sectorsize combinations
+
+source $TOP/tests/common
+
+check_prereq btrfs-show-super
+check_prereq mkfs.btrfs
+check_prereq btrfs
+
+setup_root_helper
+prepare_test_dev
+
+test_mkfs_single()
+{
+ run_check $SUDO_HELPER $TOP/mkfs.btrfs -f "$@" $TEST_DEV
+ run_check $TOP/btrfs-show-super $TEST_DEV
+ run_check $SUDO_HELPER $TOP/btrfs check $TEST_DEV
+}
+
+# default
+test_mkfs_single
+
+# nodesize >= sectorsize
+for nodesize in 4096 8192 16384 32768 65536; do
+ for sectorsize in 4096 8192 16384 32768 65536; do
+ [ $nodesize -lt $sectorsize ] && continue
+ test_mkfs_single -n $nodesize -s $sectorsize -d single -m single
+ test_mkfs_single -n $nodesize -s $sectorsize -d single -m dup
+ done
+done
+
+# nodesize, mixed mode
+for nodesize in 4k 8k 16k 32k 64k; do
+ test_mkfs_single -n $nodesize -s $nodesize -d single -m single --mixed
+ test_mkfs_single -n $nodesize -s $nodesize -d dup -m dup --mixed
+done
diff --git a/tests/mkfs-tests/008-secorsize-nodesize-combination/test.sh b/tests/mkfs-tests/008-secorsize-nodesize-combination/test.sh
new file mode 100755
index 0000000..79cc2b2
--- /dev/null
+++ b/tests/mkfs-tests/008-secorsize-nodesize-combination/test.sh
@@ -0,0 +1,50 @@
+#!/bin/bash
+# test various sectorsize and node size combinations
+# including valid and invalid ones
+# only do mkfs and fsck check, no mounting as
+# sub/multi-pagesize is not supported yet
+
+source $TOP/tests/common
+
+check_prereq mkfs.btrfs
+check_prereq btrfs
+
+prepare_test_dev
+
+# disable mixed bg to avoid sectorsize == nodesize check
+features="^mixed-bg"
+
+# caller need to check whether the combination is valid
+do_test()
+{
+ sectorsize=$1
+ nodesize=$2
+ run_mayfail $TOP/mkfs.btrfs -O $features -n $nodesize -s $sectorsize \
+ $TEST_DEV
+ ret=$?
+ if [ $ret == 0 ]; then
+ run_check $TOP/btrfs check $TEST_DEV
+ fi
+ return $ret
+}
+
+# Invalid: Unaligned sectorsize and nodesize
+do_test 8191 8191 && _fail
+
+# Invalid: Aligned sectorsize with unaligned nodesize
+do_test 4k 16385 && _fail
+
+# Invalid: Ungliend sectorsize with aligned nodesize
+do_test 8191 16k && _fail
+
+# Valid: Aligned sectorsize and nodesize
+do_test 4k 16k || _fail
+
+# Invalid: Sectorsize larger than nodesize
+do_test 8k 4k && _fail
+
+# Invalid: too large nodesize
+do_test 16k 128k && _fail
+
+# Valid: large sectorsize
+do_test 64k 64k || _fail
diff --git a/utils.c b/utils.c
index d546bea..3df8b42 100644
--- a/utils.c
+++ b/utils.c
@@ -37,6 +37,7 @@
#include <sys/vfs.h>
#include <sys/statfs.h>
#include <linux/magic.h>
+#include <getopt.h>
#include "kerncompat.h"
#include "radix-tree.h"
@@ -47,6 +48,7 @@
#include "utils.h"
#include "volumes.h"
#include "ioctl.h"
+#include "commands.h"
#ifndef BLKDISCARD
#define BLKDISCARD _IO(0x12,119)
@@ -154,7 +156,7 @@ int test_uuid_unique(char *fs_uuid)
blkid_dev dev = NULL;
blkid_cache cache = NULL;
- if (blkid_get_cache(&cache, 0) < 0) {
+ if (blkid_get_cache(&cache, NULL) < 0) {
printf("ERROR: lblkid cache get failed\n");
return 1;
}
@@ -182,7 +184,7 @@ int test_uuid_unique(char *fs_uuid)
int make_btrfs(int fd, struct btrfs_mkfs_config *cfg)
{
struct btrfs_super_block super;
- struct extent_buffer *buf = NULL;
+ struct extent_buffer *buf;
struct btrfs_root_item root_item;
struct btrfs_disk_key disk_key;
struct btrfs_extent_item *extent_item;
@@ -204,6 +206,10 @@ int make_btrfs(int fd, struct btrfs_mkfs_config *cfg)
BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA);
u64 num_bytes;
+ buf = malloc(sizeof(*buf) + max(cfg->sectorsize, cfg->nodesize));
+ if (!buf)
+ return -ENOMEM;
+
first_free = BTRFS_SUPER_INFO_OFFSET + cfg->sectorsize * 2 - 1;
first_free &= ~((u64)cfg->sectorsize - 1);
@@ -249,8 +255,6 @@ int make_btrfs(int fd, struct btrfs_mkfs_config *cfg)
if (cfg->label)
strncpy(super.label, cfg->label, BTRFS_LABEL_SIZE - 1);
- buf = malloc(sizeof(*buf) + max(cfg->sectorsize, cfg->nodesize));
-
/* create the tree of root objects */
memset(buf->data, 0, cfg->nodesize);
buf->len = cfg->nodesize;
@@ -550,12 +554,12 @@ int make_btrfs(int fd, struct btrfs_mkfs_config *cfg)
/* and write out the super block */
BUG_ON(sizeof(super) > cfg->sectorsize);
- memset(buf->data, 0, cfg->sectorsize);
+ memset(buf->data, 0, BTRFS_SUPER_INFO_SIZE);
memcpy(buf->data, &super, sizeof(super));
- buf->len = cfg->sectorsize;
+ buf->len = BTRFS_SUPER_INFO_SIZE;
csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
- ret = pwrite(fd, buf->data, cfg->sectorsize, cfg->blocks[0]);
- if (ret != cfg->sectorsize) {
+ ret = pwrite(fd, buf->data, BTRFS_SUPER_INFO_SIZE, cfg->blocks[0]);
+ if (ret != BTRFS_SUPER_INFO_SIZE) {
ret = (ret < 0 ? -errno : -EIO);
goto out;
}
@@ -1170,6 +1174,34 @@ static int is_loop_device (const char* device) {
MAJOR(statbuf.st_rdev) == LOOP_MAJOR);
}
+/*
+ * Takes a loop device path (e.g. /dev/loop0) and returns
+ * the associated file (e.g. /images/my_btrfs.img) using
+ * loopdev API
+ */
+static int resolve_loop_device_with_loopdev(const char* loop_dev, char* loop_file)
+{
+ int fd;
+ int ret;
+ struct loop_info64 lo64;
+
+ fd = open(loop_dev, O_RDONLY | O_NONBLOCK);
+ if (fd < 0)
+ return -errno;
+ ret = ioctl(fd, LOOP_GET_STATUS64, &lo64);
+ if (ret < 0) {
+ ret = -errno;
+ goto out;
+ }
+
+ memcpy(loop_file, lo64.lo_file_name, sizeof(lo64.lo_file_name));
+ loop_file[sizeof(lo64.lo_file_name)] = 0;
+
+out:
+ close(fd);
+
+ return ret;
+}
/* Takes a loop device path (e.g. /dev/loop0) and returns
* the associated file (e.g. /images/my_btrfs.img) */
@@ -1185,8 +1217,15 @@ static int resolve_loop_device(const char* loop_dev, char* loop_file,
if (!realpath(loop_dev, real_loop_dev))
return -errno;
snprintf(p, PATH_MAX, "/sys/block/%s/loop/backing_file", strrchr(real_loop_dev, '/'));
- if (!(f = fopen(p, "r")))
+ if (!(f = fopen(p, "r"))) {
+ if (errno == ENOENT)
+ /*
+ * It's possibly a partitioned loop device, which is
+ * resolvable with loopdev API.
+ */
+ return resolve_loop_device_with_loopdev(loop_dev, loop_file);
return -errno;
+ }
snprintf(fmt, 20, "%%%i[^\n]", max_len-1);
ret = fscanf(f, fmt, loop_file);
@@ -1479,7 +1518,6 @@ int btrfs_register_one_device(const char *fname)
struct btrfs_ioctl_vol_args args;
int fd;
int ret;
- int e;
fd = open("/dev/btrfs-control", O_RDWR);
if (fd < 0) {
@@ -1491,11 +1529,10 @@ int btrfs_register_one_device(const char *fname)
memset(&args, 0, sizeof(args));
strncpy_null(args.name, fname);
ret = ioctl(fd, BTRFS_IOC_SCAN_DEV, &args);
- e = errno;
if (ret < 0) {
fprintf(stderr, "ERROR: device scan failed '%s' - %s\n",
- fname, strerror(e));
- ret = -e;
+ fname, strerror(errno));
+ ret = -errno;
}
close(fd);
return ret;
@@ -1516,7 +1553,7 @@ int btrfs_register_all_devices(void)
list_for_each_entry(fs_devices, all_uuids, list) {
list_for_each_entry(device, &fs_devices->devices, dev_list) {
- if (strlen(device->name) != 0) {
+ if (*device->name) {
err = btrfs_register_one_device(device->name);
if (err < 0)
return err;
@@ -2042,7 +2079,7 @@ int get_device_info(int fd, u64 devid,
memset(&di_args->uuid, '\0', sizeof(di_args->uuid));
ret = ioctl(fd, BTRFS_IOC_DEV_INFO, di_args);
- return ret ? -errno : 0;
+ return ret < 0 ? -errno : 0;
}
static u64 find_max_device_id(struct btrfs_ioctl_search_args *search_args,
@@ -2426,7 +2463,7 @@ static int group_profile_devs_min(u64 flag)
}
int test_num_disk_vs_raid(u64 metadata_profile, u64 data_profile,
- u64 dev_cnt, int mixed)
+ u64 dev_cnt, int mixed, int ssd)
{
u64 allowed = 0;
@@ -2467,11 +2504,9 @@ int test_num_disk_vs_raid(u64 metadata_profile, u64 data_profile,
return 1;
}
- if (!mixed && (data_profile & BTRFS_BLOCK_GROUP_DUP)) {
- fprintf(stderr,
- "ERROR: DUP for data is allowed only in mixed mode\n");
- return 1;
- }
+ warning_on(!mixed && (data_profile & BTRFS_BLOCK_GROUP_DUP) && ssd,
+ "DUP may not actually lead to 2 copies on the device, see manual page");
+
return 0;
}
@@ -2566,7 +2601,7 @@ int btrfs_scan_lblkid(void)
if (btrfs_scan_done)
return 0;
- if (blkid_get_cache(&cache, 0) < 0) {
+ if (blkid_get_cache(&cache, NULL) < 0) {
printf("ERROR: lblkid cache get failed\n");
return 1;
}
@@ -2661,17 +2696,15 @@ int lookup_ino_rootid(int fd, u64 *rootid)
{
struct btrfs_ioctl_ino_lookup_args args;
int ret;
- int e;
memset(&args, 0, sizeof(args));
args.treeid = 0;
args.objectid = BTRFS_FIRST_FREE_OBJECTID;
ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args);
- e = errno;
- if (ret) {
+ if (ret < 0) {
fprintf(stderr, "ERROR: Failed to lookup root id - %s\n",
- strerror(e));
+ strerror(errno));
return ret;
}
@@ -2768,11 +2801,11 @@ int test_issubvolname(const char *name)
}
/*
- * test if path is a directory
- * this function return
- * 0-> path exists but it is not a directory
- * 1-> path exists and it is a directory
- * -1 -> path is unaccessible
+ * Test if path is a directory
+ * Returns:
+ * 0 - path exists but it is not a directory
+ * 1 - path exists and it is a directory
+ * < 0 - error
*/
int test_isdir(const char *path)
{
@@ -2780,10 +2813,10 @@ int test_isdir(const char *path)
int ret;
ret = stat(path, &st);
- if(ret < 0 )
- return -1;
+ if (ret < 0)
+ return -errno;
- return S_ISDIR(st.st_mode);
+ return !!S_ISDIR(st.st_mode);
}
void units_set_mode(unsigned *units, unsigned mode)
@@ -3088,3 +3121,29 @@ int string_is_numerical(const char *str)
return 0;
return 1;
}
+
+/*
+ * Preprocess @argv with getopt_long to reorder options and consume the "--"
+ * option separator.
+ * Unknown short and long options are reported, optionally the @usage is printed
+ * before exit.
+ */
+void clean_args_no_options(int argc, char *argv[], const char * const *usagestr)
+{
+ static const struct option long_options[] = {
+ {NULL, 0, NULL, 0}
+ };
+
+ while (1) {
+ int c = getopt_long(argc, argv, "", long_options, NULL);
+
+ if (c < 0)
+ break;
+
+ switch (c) {
+ default:
+ if (usagestr)
+ usage(usagestr);
+ }
+ }
+}
diff --git a/utils.h b/utils.h
index 33b410c..d53357a 100644
--- a/utils.h
+++ b/utils.h
@@ -109,12 +109,16 @@ void btrfs_parse_features_to_string(char *buf, u64 flags);
struct btrfs_mkfs_config {
char *label;
char *fs_uuid;
+ char *chunk_uuid;
u64 blocks[8];
u64 num_bytes;
u32 nodesize;
u32 sectorsize;
u32 stripesize;
u64 features;
+
+ /* Super bytenr after make_btrfs */
+ u64 super_bytenr;
};
int make_btrfs(int fd, struct btrfs_mkfs_config *cfg);
@@ -167,7 +171,7 @@ int test_dev_for_mkfs(char *file, int force_overwrite);
int get_label_mounted(const char *mount_path, char *labelp);
int get_label_unmounted(const char *dev, char *label);
int test_num_disk_vs_raid(u64 metadata_profile, u64 data_profile,
- u64 dev_cnt, int mixed);
+ u64 dev_cnt, int mixed, int ssd);
int group_profile_max_safe_loss(u64 flags);
int is_vol_small(char *file);
int csum_tree_block(struct btrfs_root *root, struct extent_buffer *buf,
@@ -247,7 +251,7 @@ int btrfs_check_nodesize(u32 nodesize, u32 sectorsize, u64 features);
const char *get_argv0_buf(void);
-#define HELPINFO_OUTPUT_UNIT \
+#define HELPINFO_UNITS_LONG \
"--raw raw numbers in bytes", \
"--human-readable human friendly numbers, base 1024 (default)", \
"--iec use 1024 as a base (KiB, MiB, GiB, TiB)", \
@@ -257,7 +261,7 @@ const char *get_argv0_buf(void);
"--gbytes show sizes in GiB, or GB with --si", \
"--tbytes show sizes in TiB, or TB with --si"
-#define HELPINFO_OUTPUT_UNIT_DF \
+#define HELPINFO_UNITS_SHORT_LONG \
"-b|--raw raw numbers in bytes", \
"-h|--human-readable", \
" human friendly numbers, base 1024 (default)", \
@@ -270,8 +274,10 @@ 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);
+void clean_args_no_options(int argc, char *argv[], const char * const *usage);
int string_is_numerical(const char *str);
+__attribute__ ((format (printf, 1, 2)))
static inline void warning(const char *fmt, ...)
{
va_list args;
@@ -283,6 +289,7 @@ static inline void warning(const char *fmt, ...)
fputc('\n', stderr);
}
+__attribute__ ((format (printf, 1, 2)))
static inline void error(const char *fmt, ...)
{
va_list args;
@@ -294,6 +301,7 @@ static inline void error(const char *fmt, ...)
fputc('\n', stderr);
}
+__attribute__ ((format (printf, 2, 3)))
static inline int warning_on(int condition, const char *fmt, ...)
{
va_list args;
@@ -310,6 +318,7 @@ static inline int warning_on(int condition, const char *fmt, ...)
return 1;
}
+__attribute__ ((format (printf, 2, 3)))
static inline int error_on(int condition, const char *fmt, ...)
{
va_list args;
diff --git a/version.sh b/version.sh
index f8d022a..a01786e 100755
--- a/version.sh
+++ b/version.sh
@@ -6,7 +6,7 @@
# Copyright 2008, Oracle
# Released under the GNU GPLv2
-v="v4.3"
+v="v4.4"
opt=$1
diff --git a/volumes.c b/volumes.c
index 83ddd16..a94be0e 100644
--- a/volumes.c
+++ b/volumes.c
@@ -252,21 +252,14 @@ int btrfs_scan_one_device(int fd, const char *path,
u64 *total_devs, u64 super_offset, int super_recover)
{
struct btrfs_super_block *disk_super;
- char *buf;
+ char buf[BTRFS_SUPER_INFO_SIZE];
int ret;
u64 devid;
- buf = malloc(4096);
- if (!buf) {
- ret = -ENOMEM;
- goto error;
- }
disk_super = (struct btrfs_super_block *)buf;
ret = btrfs_read_dev_super(fd, disk_super, super_offset, super_recover);
- if (ret < 0) {
- ret = -EIO;
- goto error_brelse;
- }
+ if (ret < 0)
+ return -EIO;
devid = btrfs_stack_device_id(&disk_super->dev_item);
if (btrfs_super_flags(disk_super) & BTRFS_SUPER_FLAG_METADUMP)
*total_devs = 1;
@@ -275,9 +268,6 @@ int btrfs_scan_one_device(int fd, const char *path,
ret = device_list_add(path, disk_super, devid, fs_devices_ret);
-error_brelse:
- free(buf);
-error:
return ret;
}
@@ -1175,8 +1165,8 @@ int btrfs_num_copies(struct btrfs_mapping_tree *map_tree, u64 logical, u64 len)
return ret;
}
-int btrfs_next_metadata(struct btrfs_mapping_tree *map_tree, u64 *logical,
- u64 *size)
+int btrfs_next_bg(struct btrfs_mapping_tree *map_tree, u64 *logical,
+ u64 *size, u64 type)
{
struct cache_extent *ce;
struct map_lookup *map;
@@ -1189,7 +1179,7 @@ int btrfs_next_metadata(struct btrfs_mapping_tree *map_tree, u64 *logical,
return -ENOENT;
map = container_of(ce, struct map_lookup, ce);
- if (map->type & BTRFS_BLOCK_GROUP_METADATA) {
+ if (map->type & type) {
*logical = ce->start;
*size = ce->size;
return 0;
@@ -1601,6 +1591,7 @@ static int read_one_chunk(struct btrfs_root *root, struct btrfs_key *key,
struct cache_extent *ce;
u64 logical;
u64 length;
+ u64 stripe_len;
u64 devid;
u8 uuid[BTRFS_UUID_SIZE];
int num_stripes;
@@ -1609,6 +1600,33 @@ static int read_one_chunk(struct btrfs_root *root, struct btrfs_key *key,
logical = key->offset;
length = btrfs_chunk_length(leaf, chunk);
+ stripe_len = btrfs_chunk_stripe_len(leaf, chunk);
+ num_stripes = btrfs_chunk_num_stripes(leaf, chunk);
+ /* Validation check */
+ if (!num_stripes) {
+ error("invalid chunk num_stripes: %u", num_stripes);
+ return -EIO;
+ }
+ if (!IS_ALIGNED(logical, root->sectorsize)) {
+ error("invalid chunk logical %llu", logical);
+ return -EIO;
+ }
+ if (!length || !IS_ALIGNED(length, root->sectorsize)) {
+ error("invalid chunk length %llu", length);
+ return -EIO;
+ }
+ if (!is_power_of_2(stripe_len)) {
+ error("invalid chunk stripe length: %llu", stripe_len);
+ return -EIO;
+ }
+ if (~(BTRFS_BLOCK_GROUP_TYPE_MASK | BTRFS_BLOCK_GROUP_PROFILE_MASK) &
+ btrfs_chunk_type(leaf, chunk)) {
+ error("unrecognized chunk type: %llu",
+ ~(BTRFS_BLOCK_GROUP_TYPE_MASK |
+ BTRFS_BLOCK_GROUP_PROFILE_MASK) &
+ btrfs_chunk_type(leaf, chunk));
+ return -EIO;
+ }
ce = search_cache_extent(&map_tree->cache_tree, logical);
@@ -1617,7 +1635,6 @@ static int read_one_chunk(struct btrfs_root *root, struct btrfs_key *key,
return 0;
}
- num_stripes = btrfs_chunk_num_stripes(leaf, chunk);
map = kmalloc(btrfs_map_lookup_size(num_stripes), GFP_NOFS);
if (!map)
return -ENOMEM;
@@ -1805,12 +1822,14 @@ int btrfs_read_sys_array(struct btrfs_root *root)
struct extent_buffer *sb;
struct btrfs_disk_key *disk_key;
struct btrfs_chunk *chunk;
- struct btrfs_key key;
+ u8 *array_ptr;
+ unsigned long sb_array_offset;
+ int ret = 0;
u32 num_stripes;
+ u32 array_size;
u32 len = 0;
- u8 *ptr;
- u8 *array_end;
- int ret = 0;
+ u32 cur_offset;
+ struct btrfs_key key;
sb = btrfs_find_create_tree_block(root, BTRFS_SUPER_INFO_OFFSET,
BTRFS_SUPER_INFO_SIZE);
@@ -1818,37 +1837,69 @@ int btrfs_read_sys_array(struct btrfs_root *root)
return -ENOMEM;
btrfs_set_buffer_uptodate(sb);
write_extent_buffer(sb, super_copy, 0, sizeof(*super_copy));
- array_end = ((u8 *)super_copy->sys_chunk_array) +
- btrfs_super_sys_array_size(super_copy);
+ array_size = btrfs_super_sys_array_size(super_copy);
- /*
- * we do this loop twice, once for the device items and
- * once for all of the chunks. This way there are device
- * structs filled in for every chunk
- */
- ptr = super_copy->sys_chunk_array;
+ array_ptr = super_copy->sys_chunk_array;
+ sb_array_offset = offsetof(struct btrfs_super_block, sys_chunk_array);
+ cur_offset = 0;
+
+ while (cur_offset < array_size) {
+ disk_key = (struct btrfs_disk_key *)array_ptr;
+ len = sizeof(*disk_key);
+ if (cur_offset + len > array_size)
+ goto out_short_read;
- while (ptr < array_end) {
- disk_key = (struct btrfs_disk_key *)ptr;
btrfs_disk_key_to_cpu(&key, disk_key);
- len = sizeof(*disk_key);
- ptr += len;
+ array_ptr += len;
+ sb_array_offset += len;
+ cur_offset += len;
if (key.type == BTRFS_CHUNK_ITEM_KEY) {
- chunk = (struct btrfs_chunk *)(ptr - (u8 *)super_copy);
+ chunk = (struct btrfs_chunk *)sb_array_offset;
+ /*
+ * At least one btrfs_chunk with one stripe must be
+ * present, exact stripe count check comes afterwards
+ */
+ len = btrfs_chunk_item_size(1);
+ if (cur_offset + len > array_size)
+ goto out_short_read;
+
+ num_stripes = btrfs_chunk_num_stripes(sb, chunk);
+ if (!num_stripes) {
+ printk(
+ "ERROR: invalid number of stripes %u in sys_array at offset %u\n",
+ num_stripes, cur_offset);
+ ret = -EIO;
+ break;
+ }
+
+ len = btrfs_chunk_item_size(num_stripes);
+ if (cur_offset + len > array_size)
+ goto out_short_read;
+
ret = read_one_chunk(root, &key, sb, chunk, -1);
if (ret)
break;
- num_stripes = btrfs_chunk_num_stripes(sb, chunk);
- len = btrfs_chunk_item_size(num_stripes);
} else {
- BUG();
+ printk(
+ "ERROR: unexpected item type %u in sys_array at offset %u\n",
+ (u32)key.type, cur_offset);
+ ret = -EIO;
+ break;
}
- ptr += len;
+ array_ptr += len;
+ sb_array_offset += len;
+ cur_offset += len;
}
free_extent_buffer(sb);
return ret;
+
+out_short_read:
+ printk("ERROR: sys_array too short to read %u bytes at offset %u\n",
+ len, cur_offset);
+ free_extent_buffer(sb);
+ return -EIO;
}
int btrfs_read_chunk_tree(struct btrfs_root *root)
diff --git a/volumes.h b/volumes.h
index 4ecb993..c0007ad 100644
--- a/volumes.h
+++ b/volumes.h
@@ -136,6 +136,9 @@ struct map_lookup {
#define BTRFS_BALANCE_ARGS_DRANGE (1ULL << 3)
#define BTRFS_BALANCE_ARGS_VRANGE (1ULL << 4)
#define BTRFS_BALANCE_ARGS_LIMIT (1ULL << 5)
+#define BTRFS_BALANCE_ARGS_LIMIT_RANGE (1ULL << 6)
+#define BTRFS_BALANCE_ARGS_STRIPES_RANGE (1ULL << 7)
+#define BTRFS_BALANCE_ARGS_USAGE_RANGE (1ULL << 10)
/*
* Profile changing flags. When SOFT is set we won't relocate chunk if
@@ -167,8 +170,20 @@ int btrfs_map_block(struct btrfs_mapping_tree *map_tree, int rw,
u64 logical, u64 *length,
struct btrfs_multi_bio **multi_ret, int mirror_num,
u64 **raid_map_ret);
-int btrfs_next_metadata(struct btrfs_mapping_tree *map_tree, u64 *logical,
- u64 *size);
+int btrfs_next_bg(struct btrfs_mapping_tree *map_tree, u64 *logical,
+ u64 *size, u64 type);
+static inline int btrfs_next_bg_metadata(struct btrfs_mapping_tree *map_tree,
+ u64 *logical, u64 *size)
+{
+ return btrfs_next_bg(map_tree, logical, size,
+ BTRFS_BLOCK_GROUP_METADATA);
+}
+static inline int btrfs_next_bg_system(struct btrfs_mapping_tree *map_tree,
+ u64 *logical, u64 *size)
+{
+ return btrfs_next_bg(map_tree, logical, size,
+ BTRFS_BLOCK_GROUP_SYSTEM);
+}
int btrfs_rmap_block(struct btrfs_mapping_tree *map_tree,
u64 chunk_start, u64 physical, u64 devid,
u64 **logical, int *naddrs, int *stripe_len);