summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--64-btrfs-dm.rules10
-rw-r--r--CHANGES52
-rw-r--r--Documentation/btrfs-check.asciidoc93
-rw-r--r--Documentation/btrfs-convert.asciidoc1
-rw-r--r--Documentation/btrfs-device.asciidoc205
-rw-r--r--Documentation/btrfs-man5.asciidoc89
-rw-r--r--Documentation/btrfs-receive.asciidoc54
-rw-r--r--Documentation/btrfs-restore.asciidoc55
-rw-r--r--Documentation/btrfs-scrub.asciidoc51
-rw-r--r--Documentation/btrfs-select-super.asciidoc36
-rw-r--r--Documentation/btrfs-send.asciidoc49
-rw-r--r--Documentation/btrfs-subvolume.asciidoc2
-rw-r--r--Documentation/mkfs.btrfs.asciidoc23
-rw-r--r--Makefile.in30
-rwxr-xr-xautogen.sh9
-rw-r--r--backref.c4
-rw-r--r--btrfs-convert.c2089
-rw-r--r--btrfs-corrupt-block.c28
-rw-r--r--btrfs-crc.c27
-rwxr-xr-xbtrfs-debugfs2
-rw-r--r--btrfs-fragments.c62
-rw-r--r--btrfs-image.c171
-rw-r--r--btrfs-list.c25
-rw-r--r--chunk-recover.c6
-rw-r--r--cmds-balance.c6
-rw-r--r--cmds-check.c258
-rw-r--r--cmds-fi-du.c22
-rw-r--r--cmds-fi-usage.c23
-rw-r--r--cmds-filesystem.c7
-rw-r--r--cmds-inspect-tree-stats.c2
-rw-r--r--cmds-inspect.c25
-rw-r--r--cmds-property.c25
-rw-r--r--cmds-receive.c2
-rw-r--r--cmds-scrub.c2
-rw-r--r--cmds-send.c32
-rw-r--r--cmds-subvolume.c18
-rw-r--r--commands.h6
-rw-r--r--config.h.in3
-rwxr-xr-xconfigure243
-rw-r--r--configure.ac11
-rw-r--r--ctree.c38
-rw-r--r--ctree.h45
-rw-r--r--debian/changelog3
-rw-r--r--dir-item.c2
-rw-r--r--disk-io.c107
-rw-r--r--disk-io.h4
-rw-r--r--extent-cache.h2
-rw-r--r--extent-tree.c251
-rw-r--r--file.c4
-rw-r--r--inode.c4
-rw-r--r--ioctl.h6
-rw-r--r--kerncompat.h6
-rw-r--r--list_sort.c2
-rw-r--r--m4/ax_check_define.m492
-rw-r--r--mkfs.c15
-rw-r--r--props.c26
-rw-r--r--qgroup-verify.c22
-rw-r--r--qgroup-verify.h2
-rw-r--r--qgroup.c44
-rw-r--r--send-utils.c62
-rwxr-xr-xshow-blocks4
-rw-r--r--super-recover.c4
-rw-r--r--tests/README.md47
-rwxr-xr-xtests/cli-tests.sh3
-rwxr-xr-xtests/cli-tests/003-fi-resize-args/test.sh46
-rw-r--r--tests/common10
-rw-r--r--tests/common.convert182
-rwxr-xr-xtests/convert-tests.sh177
-rwxr-xr-xtests/convert-tests/001-ext2-basic/test.sh16
-rwxr-xr-xtests/convert-tests/002-ext3-basic/test.sh16
-rwxr-xr-xtests/convert-tests/003-ext4-basic/test.sh16
-rw-r--r--tests/convert-tests/004-ext2-backup-superblock-ranges/drdhdhdrd.e2image.raw.xzbin0 -> 84564 bytes
-rw-r--r--tests/convert-tests/004-ext2-backup-superblock-ranges/drdhdhrh.e2image.raw.xzbin0 -> 84568 bytes
-rw-r--r--tests/convert-tests/004-ext2-backup-superblock-ranges/hrhdhdrd.e2image.raw.xzbin0 -> 84556 bytes
-rw-r--r--tests/convert-tests/004-ext2-backup-superblock-ranges/hrhdhrh.e2image.raw.xzbin0 -> 84568 bytes
-rwxr-xr-xtests/convert-tests/004-ext2-backup-superblock-ranges/test.sh43
-rwxr-xr-xtests/convert-tests/005-delete-all-rollback/test.sh61
-rwxr-xr-xtests/fsck-tests.sh10
-rw-r--r--tests/fsck-tests/020-extent-ref-cases/keyed_block_ref.imgbin0 -> 10240 bytes
-rw-r--r--tests/fsck-tests/020-extent-ref-cases/keyed_data_ref.imgbin0 -> 4096 bytes
-rw-r--r--tests/fsck-tests/020-extent-ref-cases/no_inline_ref.imgbin0 -> 4096 bytes
-rw-r--r--tests/fsck-tests/020-extent-ref-cases/no_skinny_ref.imgbin0 -> 3072 bytes
-rw-r--r--tests/fsck-tests/020-extent-ref-cases/shared_block_ref.imgbin0 -> 23552 bytes
-rw-r--r--tests/fsck-tests/020-extent-ref-cases/shared_data_ref.imgbin0 -> 5120 bytes
-rwxr-xr-xtests/fsck-tests/020-extent-ref-cases/test.sh23
-rwxr-xr-xtests/fuzz-tests.sh3
-rw-r--r--tests/fuzz-tests/images/superblock-stripsize-bogus.raw.txt32
-rw-r--r--tests/fuzz-tests/images/superblock-stripsize-bogus.raw.xzbin0 -> 41512 bytes
-rw-r--r--tests/fuzz-tests/images/superblock-total-bytes-0.raw.txt50
-rw-r--r--tests/fuzz-tests/images/superblock-total-bytes-0.raw.xzbin0 -> 41424 bytes
-rw-r--r--tests/fuzz-tests/images/sys-chunk-stripe-len-bogus.raw.txt54
-rw-r--r--tests/fuzz-tests/images/sys-chunk-stripe-len-bogus.raw.xzbin0 -> 41440 bytes
-rw-r--r--tests/fuzz-tests/images/sys-chunk-type-bogus.raw.txt55
-rw-r--r--tests/fuzz-tests/images/sys-chunk-type-bogus.raw.xzbin0 -> 41524 bytes
-rwxr-xr-xtests/misc-tests.sh6
-rwxr-xr-xtests/misc-tests/007-subvolume-sync/test.sh4
-rwxr-xr-xtests/misc-tests/009-subvolume-sync-must-wait/test.sh2
-rwxr-xr-xtests/misc-tests/013-subvolume-sync-crash/test.sh2
-rwxr-xr-xtests/mkfs-tests.sh6
-rwxr-xr-xtests/mkfs-tests/008-secorsize-nodesize-combination/test.sh2
-rwxr-xr-xtests/test-console.sh23
-rw-r--r--ulist.c2
-rw-r--r--utils.c999
-rw-r--r--utils.h66
-rw-r--r--uuid-tree.c2
-rwxr-xr-xversion.sh2
-rw-r--r--volumes.c211
-rw-r--r--volumes.h2
109 files changed, 4568 insertions, 2212 deletions
diff --git a/.gitignore b/.gitignore
index a27cb0d..214103d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -33,6 +33,7 @@ btrfs-zero-log
btrfs-corrupt-block
btrfs-select-super
btrfs-calc-size
+btrfs-crc
btrfstune
libbtrfs.a
libbtrfs.so
@@ -42,7 +43,9 @@ library-test
library-test-static
/tests/*-tests-results.txt
+/tests/test-console.txt
/tests/test.img
+/tests/mnt/
aclocal.m4
autom4te.cache
@@ -63,7 +66,6 @@ configure
cscope.out
depcomp
libtool
-m4/*.m4
Makefile
Documentation/Makefile
missing
diff --git a/64-btrfs-dm.rules b/64-btrfs-dm.rules
new file mode 100644
index 0000000..b2e49f4
--- /dev/null
+++ b/64-btrfs-dm.rules
@@ -0,0 +1,10 @@
+SUBSYSTEM!="block", GOTO="btrfs_end"
+KERNEL!="dm-[0-9]*", GOTO="btrfs_end"
+ACTION!="add|change", GOTO="btrfs_end"
+ENV{ID_FS_TYPE}!="btrfs", GOTO="btrfs_end"
+
+# Once the device mapper symlink is created, tell btrfs about it
+# so we get the friendly name in /proc/mounts (and tools that read it)
+ENV{DM_NAME}=="?*", RUN{builtin}+="btrfs ready /dev/mapper/$env{DM_NAME}"
+
+LABEL="btrfs_end"
diff --git a/CHANGES b/CHANGES
index 6186808..db90058 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,43 @@
+btrfs-progs-4.6.1 (2016-06-24)
+ * fi resize: negative resize argument accepted again (broken
+ * qgroup rescan: fix skipping when rescan is in progress
+ * mkfs: initialize stripesize to correct value
+ * testsuite updates, mostly convert tests
+ * documentation updates
+ * btrfs-device, btrfs-restore manual pages enhanced
+ * misc fixups
+
+btrfs-progs-4.6 (2016-06-10)
+ * convert - major rewrite:
+ * fix a long-standing bug that led to mixing data blocks into metadata block
+ groups
+ * the workaround was to do full balance after conversion, which was
+ recommended practice anyway
+ * explicitly set the lowest supported version of e2fstools to 1.41
+ * provide and install udev rules file that addresses problems with device
+ mapper devices, renames after removal
+ * send: new option: quiet
+ * dev usage: report slack space (device size minus filesystem area on the dev)
+ * image: support DUP
+ * build: short options to enable debugging builds
+ * other:
+ * code cleanups
+ * build fixes
+ * more tests and other enhancements
+
+btrfs-progs-4.5.3 (2016-05-11)
+ * ioctl: fix unaligned access in buffer from TREE_SEARCH; might cause SIGBUS
+ on architectures that do not support unaligned access and do not performa
+ any fixups
+ * improved validation checks of superblock and chunk-related structures
+ * subvolume sync: fix handling of -s option
+ * balance: adjust timing of safety delay countdown with --full-balance
+ * rescue super-recover: fix reversed condition check
+ * check: fix bytes_used accounting
+ * documentation updates: mount options, scrub, send, receive, select-super,
+ check, mkfs
+ * testing: new fuzzed images, for superblock and chunks
+
btrfs-progs-4.5.2 (2016-05-02)
* new/moved command: btrfs-calc-stats -> btrfs inspect tree-stats
* check: fix false alert for metadata blocks crossing stripe boundary
@@ -82,8 +122,8 @@ btrfs-progs-4.4 (2016-01-18)
* check: fix a false alert where extent record has wrong metadata flag
* improved stability on fuzzed/crafted images when reading sys array in
superblock
- * build: the 'ar' tool is properly deteced during cross-compilation
- * debug-tree: option -t understands ids for tree root and chnuk tree
+ * build: the 'ar' tool is properly detected during cross-compilation
+ * debug-tree: option -t understands ids for tree root and chunk tree
* preparatory work for btrfs-convert rewrite
* sparse, gcc warning fixes
* more memory allocation failure handling
@@ -131,7 +171,7 @@ btrfs-progs-4.3 (2015-11-06)
subvolume
* other:
* check: add progress indicator
- * scrub: enahced error message
+ * scrub: enhanced error message
* show-super: read superblock from a given offset
* add README
* docs: update manual page for mkfs.btrfs, btrfstune, balance,
@@ -155,7 +195,7 @@ btrfs-progs-4.3 (2015-11-06)
btrfs-progs-4.2.3 (2015-10-19)
* subvol sync: make it actually work again: it's been broken since 4.1.2,
- due to a reversed condition it returned immediatelly instead of waiting
+ due to a reversed condition it returned immediately instead of waiting
* scanning: do not scan already discovered filesystems (minor optimization)
* convert: better error message in case the filesystem is not finalized
* restore: off-by-one symlink path check fix
@@ -226,7 +266,7 @@ btrfs-progs-4.1.1 (2015-07-10) -- Do not use this version!
* documentation updates
* debug-tree: print nbytes
* test: image for corrupted nbytes
- * corupt-block: let it kill nbytes
+ * corrupt-block: let it kill nbytes
btrfs-progs-4.1 (2015-06-22)
Bugfixes:
@@ -267,7 +307,7 @@ btrfs-progs-4.1 (2015-06-22)
* debug tree: print key names according to their C name
New:
- * rescure zero-log
+ * rescue zero-log
* btrfsune:
* rewrite uuid on a filesystem image
* new option to turn on NO_HOLES incompat feature
diff --git a/Documentation/btrfs-check.asciidoc b/Documentation/btrfs-check.asciidoc
index 7371a23..74a2ad2 100644
--- a/Documentation/btrfs-check.asciidoc
+++ b/Documentation/btrfs-check.asciidoc
@@ -11,42 +11,87 @@ SYNOPSIS
DESCRIPTION
-----------
-*btrfs check* is used to check or repair an unmounted btrfs filesystem.
-NOTE: Since btrfs is under development, the *btrfs check* capabilities are
-continuously enhanced. It's highly recommended to read the following btrfs
-wiki before executing *btrfs check* with '--repair' option: +
-https://btrfs.wiki.kernel.org/index.php/Btrfsck
+The filesystem checker is used to verify structural integrity of a filesystem
+and attempt to repair it if requested. The filesystem must be unmounted.
+
+By default, *btrfs check* will not modify the device but you can reaffirm that
+by the option '--readonly'.
*btrfsck* is an alias of *btrfs check* command and is now deprecated.
-OPTIONS
--------
--s|--super <superblock>::
-use <superblock>th superblock copy, valid values are 0 up to 2 if the
-respective superblock offset is within the filesystem
+WARNING: Do not use '--repair' unless you are adviced to by a developer, an
+experienced user or accept the fact that 'fsck' cannot possibly fix all sorts
+of damage that could happen to a filesystem because of software and hardware
+bugs.
+
+The structural integrity check verifies if internal filesystem objects or
+data structures satisfy the constraints, point to the right objects or are
+correctly connected together.
+
+There are several cross checks that can detect wrong reference counts of shared
+extents, backrefrences, missing extents of inodes, directory and inode
+connectivity etc.
+
+The amount of memory required can be high, depending on the size of the
+filesystem, smililarly the run time.
+
+SAFE OR ADVISORY OPTIONS
+------------------------
+
-b|--backup::
-use the first backup roots stored in the superblock that is valid
---repair::
-try to repair the filesystem
---readonly::
-run in read-only mode (default)
---init-csum-tree::
-create a new CRC tree and recalculate all checksums
---init-extent-tree::
-create a new extent tree
+use the first valid set of backup roots stored in the superblock
++
+This can be combined with '--super' if some of the superblocks are damaged.
+
--check-data-csum::
verify checksums of data blocks
++
+This expects that the filesystem is otherwise
+OK, so this is basically and offline 'scrub' but does not repair data from
+spare coipes.
+
+--chunk-root <bytenr>::
+use the given offset 'bytenr' for the chunk tree root
+
+-E|--subvol-extents <subvolid>::
+show extent state for the given subvolume
+
-p|--progress::
indicate progress at various checking phases
+
--qgroup-report::
verify qgroup accounting and compare against filesystem accounting
--E|--subvol-extents <subvolid>::
-show extent state for the given subvolume
+
-r|--tree-root <bytenr>::
-use the given bytenr for the tree root
---chunk-root <bytenr>::
-use the given bytenr for the chunk tree root
+use the given offset 'bytenr' for the tree root
+
+--readonly::
+(default)
+run in read-only mode, this option exists to calm potential panic when users
+are going to run the checker
+
+-s|--super <superblock>::
+use 'superblock'th superblock copy, valid values are 0, 1 or 2 if the
+respective superblock offset is within the device size
++
+This can be used to use a different starting point if some of the primary
+superblock is damaged.
+
+DANGEROUS OPTIONS
+-----------------
+
+--repair::
+enable the repair mode and attempt to fix problems where possible
+--init-csum-tree::
+create a new checksum tree and recalculate checksums in all files
++
+NOTE: Do not blindly use this option to fix checksum mismatch problems.
+
+--init-extent-tree::
+build the extent tree from scratch
++
+NOTE: Do not use unless you know what you're doing.
EXIT STATUS
-----------
diff --git a/Documentation/btrfs-convert.asciidoc b/Documentation/btrfs-convert.asciidoc
index 28f9a39..ab3577d 100644
--- a/Documentation/btrfs-convert.asciidoc
+++ b/Documentation/btrfs-convert.asciidoc
@@ -90,6 +90,7 @@ are supported by old kernels. To disable a feature, prefix it with '^'.
To see all available features that btrfs-convert supports run:
+
+btrfs-convert -O list-all+
++
-p|--progress::
show progress of conversion, on by default
--no-progress::
diff --git a/Documentation/btrfs-device.asciidoc b/Documentation/btrfs-device.asciidoc
index edd9b98..d05fc45 100644
--- a/Documentation/btrfs-device.asciidoc
+++ b/Documentation/btrfs-device.asciidoc
@@ -3,7 +3,7 @@ btrfs-device(8)
NAME
----
-btrfs-device - control btrfs devices
+btrfs-device - manage devices of btrfs filesystems
SYNOPSIS
--------
@@ -11,95 +11,102 @@ SYNOPSIS
DESCRIPTION
-----------
-*btrfs device* is used to control the btrfs devices, since btrfs can be used
-across several devices, *btrfs device* is used for multiple device management.
+The *btrfs device* command group is used to manage devices of the btrfs filesystems.
DEVICE MANAGEMENT
-----------------
-Btrfs filesystem is capable to manage multiple devices.
+Btrfs filesystem can be created on top of single or multiple block devices.
+Data and metadata are organized in allocation profiles with various redundancy
+policies. There's some similarity with traditional RAID levels, but this could
+be confusing to users familiar with the traditional meaning. Due to the
+similarity, the RAID terminology is widely used in the documentation. See
+`mkfs.btrfs`(9) for more details and the exact profile capabilities and
+constraints.
-Btrfs filesystem uses different profiles to manage different RAID level, and
-use balance to rebuild chunks, also devices can be added/removed/replace
-online.
+The device management works on a mounted filesystem. Devices can be added,
+removed or replaced, by commands profided by *btrfs device* and *btrfs replace*.
+
+The profiles can be also changed, provided there's enough workspace to do the
+conversion, using the *btrfs balance* comand and namely the filter 'convert'.
Profile::
-Btrfs filesystem uses data/metadata profiles to manage allocation/duplication
-mechanism. +
-Profiles like RAID level can be assigned to data and metadata separately.
-+
-See `mkfs.btrfs`(8) for more details.
+A profile describes an allocation policy based on the redundancy/replication
+constrants in connection with the number of devices. The profile applies to
+data and metadata block groups separately.
RAID level::
-Btrfs filesystem supports most of the standard RAID level: 0/1/5/6/10. +
-RAID levels can be assigned at mkfs time or online.
-+
-See `mkfs.btrfs`(8) for mkfs time RAID level assign and `btrfs-balance`(8) for
-online RAID level assign.
-+
-NOTE: Since btrfs is under heavy development especially the RAID5/6 support,
-it is *highly* recommended to read the follow btrfs wiki page to get more
-updated details on RAID5/6: +
-https://btrfs.wiki.kernel.org/index.php/RAID56
-
-Balance::
-`btrfs-balance`(8) subcommand can be used to balance or rebuild chunks to the
-desired profile.
-+
-Due to the fact that balance can rebuild/recovery chunks according to its RAID
-duplication if possible, so when using RAID1/5/6/10 with some devices failed
-and you just added a new device to btrfs using `btrfs-device`(8), you should
-run `btrfs-balance`(8) to rebuild the chunks.
-+
-See `btrfs-balance`(8) for more details.
+Where applicable, the level refers to a profile that matches constraints of the
+standard RAID levels. At the moment the supported ones are: RAID0, RAID1,
+RAID10, RAID5 and RAID6.
-Device add/remove/replace::
-Device can be added/removed using `btrfs-device`(8) subcommand and replaced
-using `btrfs-replace`(8).
-+
-When device is removed or replaced, btrfs will do the chunk rebuild if needed.
-+
-See `btrfs-replace`(8) man page for more details on device replace.
+See the section *TYPICAL USECASES* for some examples.
SUBCOMMAND
----------
*add* [-Kf] <dev> [<dev>...] <path>::
Add device(s) to the filesystem identified by <path>.
+
-If applicable, a whole device discard (TRIM) operation is performed.
+If applicable, a whole device discard (TRIM) operation is performed prior to
+adding the device. A device with existing filesystem detected by `blkid`(8)
+will prevent device addition and has to be forced. Alternatively the filesystem
+can be wiped from the device using eg. the `wipefs`(8) tool.
++
+The operation is instant and does not affect existing data. The operation merely
+adds the device to the filesystem structures and creates some block groups
+headers.
+
`Options`
+
-K|--nodiscard::::
-do not perform discard by default
+do not perform discard (TRIM) by default
-f|--force::::
force overwrite of existing filesystem on the given disk(s)
*remove* <dev>|<devid> [<dev>|<devid>...] <path>::
-Remove device(s) from a filesystem identified by <path>.
+Remove device(s) from a filesystem identified by <path>
++
+Device removal must satisfy the profile constraints, otherwise the command
+fails. The filesystem must be converted to profile(s) that would allow the
+removal. This can typically happen when going down from 2 devices to 1 and
+using the RAID1 profile. See the example section below.
++
+The operation can take long as it needs to move all data from the device.
++
+NOTE: It is not possible to delete the device that was used to mount the
+filesystem. This is a limitation given by the VFS.
*delete* <dev>|<devid> [<dev>|<devid>...] <path>::
Alias of remove kept for backward compatibility
*ready* <device>::
-Check device to see if it has all of it's devices in cache for mounting.
+Wait until all devices of a multiple-device filesystem are scanned and registered
+within the kernel module.
*scan* [(--all-devices|-d)|<device> [<device>...]]::
-Scan devices for a btrfs filesystem.
+Scan devices for a btrfs filesystem and register them with the kernel module.
+This allows mounting multiple-device filesystem by specifying just one from the
+whole group.
++
+If no devices are passed, all block devices that blkid reports to contain btrfs
+are scanned.
+
-If one or more devices are passed, these are scanned for a btrfs filesystem.
-If no devices are passed, btrfs uses block devices containing btrfs
-filesystem as listed by blkid.
-Finally, '--all-devices' or '-d' is the deprecated option. If it is passed,
-its behavior is the same as if no devices are passed.
+The options '--all-devices' or '-d' are deprecated and kept for backward compatibility.
+If used, behavior is the same as if no devices are passed.
++
+The command can be run repeatedly. Devices that have been already registered
+remain as such. Reloading the kernel module will drop this information. There's
+an alternative way of mounting multiple-device filesystem without the need for
+prior scanning. See the mount option 'device'.
*stats* [-z] <path>|<device>::
-Read and print the device IO stats for all mounted devices of the filesystem
-identified by <path> or for a single <device>.
+Read and print the device IO error statistics for all devices of the given
+filesystem identified by <path> or for a single <device>. See section *DEVICE
+STATS* for more information.
+
`Options`
+
-z::::
-Reset stats to zero after reading them.
+Print the stats and reset the values to zero afterwards.
*usage* [options] <path> [<path>...]::
Show detailed information about internal allocations in devices.
@@ -127,6 +134,98 @@ show sizes in TiB, or TB with --si
If conflicting options are passed, the last one takes precedence.
+TYPICAL USECASES
+----------------
+
+STARTING WITH A SINGLE-DEVICE FILESYSTEM
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Assume we've created a filesystem on a block device '/dev/sda' with profile
+'single/single' (data/metadata), the device size is 50GiB and we've used the
+whole device for the filesystem. The mount point is '/mnt'.
+
+The amount of data stored is 16GiB, metadata have allocated 2GiB.
+
+==== ADD NEW DEVICE ====
+
+We want to increase the total size of the filesystem and keep the profiles. The
+size of the new device '/dev/sdb' is 100GiB.
+
+ $ btrfs device add /dev/sdb /mnt
+
+The amount of free data space increases by less than 100GiB, some space is
+allocated for metadata.
+
+==== CONVERT TO RAID1 ====
+
+Now we want to increase the redundancy level of both data and metadata, but
+we'll do that in steps. Note, that the device sizes are not equal and we'll use
+that to show the capabilities of split data/metadata and independent profiles.
+
+The constraint for RAID1 gives us at most 50GiB of usable space and exactly 2
+copies will be stored on the devices.
+
+First we'll convert the metadata. As the metadata occupy less than 50GiB and
+there's enough workspace for the conversion process, we can do:
+
+ $ btrfs balance start -mconvert=raid1 /mnt
+
+This operation can take a while as the metadata have to be moved and all block
+pointers updated. Depending on the physical locations of the old and new
+blocks, the disk seeking is the key factor affecting performance.
+
+You'll note that the system block group has been also converted to RAID1, this
+normally happens as the system block group also holds metadata (the physical to
+logial mappings).
+
+What changed:
+
+* available data space decreased by 3GiB, usable rougly (50 - 3) + (100 - 3) = 144 GiB
+* metadata redundancy increased
+
+IOW, the unequal device sizes allow for combined space for data yet improved
+redundancy for metadata. If we decide to increase redundancy of data as well,
+we're going to lose 50GiB of the second device for obvious reasons.
+
+ $ btrfs balance start -dconvert=raid1 /mnt
+
+The balance process needs some workspace (ie. a free device space without any
+data or metadata block groups) so the command could fail if there's too much
+data or the block groups occupy the whole first device.
+
+The device size of '/dev/sdb' as seen by the filesystem remains unchanged, but
+the logical space from 50-100GiB will be unused.
+
+DEVICE STATS
+------------
+
+The device stats keep persistent record of several error classes related to
+doing IO. The current values are printed at mount time and updated during
+filesystem lifetime or from a scrub run.
+
+ $ btrfs device stats /dev/sda3
+ [/dev/sda3].write_io_errs 0
+ [/dev/sda3].read_io_errs 0
+ [/dev/sda3].flush_io_errs 0
+ [/dev/sda3].corruption_errs 0
+ [/dev/sda3].generation_errs 0
+
+write_io_errs::
+Failed writes to the block devices, means that the layers beneath the
+filesystem were not able to satisfy the write request.
+read_io_errors::
+Read request analogy to write_io_errs.
+flush_io_errs::
+Number of failed writes with the 'FLUSH' flag set. The flushing is a method of
+forcing a particular order between write requests and is crucial for
+implementing crash consistency. In case of btrfs, all the metadata blocks must
+be permanently stored on the block device before the superblock is written.
+corruption_errs::
+A block checksum mismatched or a corrupted metadata header was found.
+generation_errs::
+The block generation does not match the expected value (eg. stored in the
+parent node).
+
EXIT STATUS
-----------
*btrfs device* returns a zero exit status if it succeeds. Non zero is
diff --git a/Documentation/btrfs-man5.asciidoc b/Documentation/btrfs-man5.asciidoc
index e2eea26..7dd323f 100644
--- a/Documentation/btrfs-man5.asciidoc
+++ b/Documentation/btrfs-man5.asciidoc
@@ -27,6 +27,9 @@ options please refer to `mount`(8) manpage. The options are sorted alphabeticall
+
Enable/disable support for Posix Access Control Lists (ACLs). See the
`acl`(5) manual page for more information about ACLs.
++
+The support for ACL is build-time configurable (BTRFS_FS_POSIX_ACL) and
+mount fails if 'acl' is requested but the feature is not compiled in.
*alloc_start='bytes'*::
(default: 1M, minimum: 1M)
@@ -69,7 +72,7 @@ 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
+scheduler to reorder requests in a 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
unwritten at the time the new superblock is stored permanently, expecting that
@@ -121,8 +124,11 @@ but a warning is printed if it's more than 300 seconds (5 minutes).
+
Control BTRFS file data compression. Type may be specified as 'zlib',
'lzo' or 'no' (for no compression, used for remounting). If no type
-is specified, 'zlib' is used. If compress-force is specified,
-all files will be compressed, whether or not they compress well.
+is specified, 'zlib' is used. If 'compress-force' is specified,
+all files will be compressed, whether or not they compress well. Otherwise
+some simple heuristics are applied to detect an incompressible file. If the
+first blocks written to a file are not compressible, the whole file is
+permanently marked to skip compression.
+
NOTE: If compression is enabled, 'nodatacow' and 'nodatasum' are disabled.
@@ -133,6 +139,8 @@ NOTE: If compression is enabled, 'nodatacow' and 'nodatasum' are disabled.
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)).
++
+NOTE: If 'nodatacow' or 'nodatasum' are enabled, compression is disabled.
*datasum*::
*nodatasum*::
@@ -142,6 +150,8 @@ 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)).
++
+NOTE: If 'nodatacow' or 'nodatasum' are enabled, compression is disabled.
*degraded*::
(default: off)
@@ -171,15 +181,17 @@ underlying device, the operation may severely hurt performance in case the TRIM
operation is synchronous (eg. with SATA devices up to revision 3.0).
+
If discarding is not necessary to be done at the block freeing time, there's
-*fstrim* tool that lets the filesystem discard all free blocks in a batch,
-possibly not much interfering with other operations.
+`fstrim` tool that lets the filesystem discard all free blocks in a batch,
+possibly not much interfering with other operations. Also, the the device may
+ignore the TRIM command if the range is too small, so running the batch discard
+can actually discard the blocks.
*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.
+be noisy if the system reaches near-full state.
*fatal_errors='action'*::
(since: 3.4, default: bug)
@@ -231,20 +243,31 @@ the option.
NOTE: Defaults to off due to a potential overflow problem when the free space
checksums don't fit inside a single page.
+*logreplay*::
+*nologreplay*::
+(default: on, even read-only)
++
+Enable/disable log replay at mount time. See also 'treelog'.
++
+WARNING: currently, the tree log is replayed even with a read-only mount! To
+disable that behaviour, mount also with 'nologreplay'.
+
*max_inline='bytes'*::
-(default: min(8192, page size) )
+(default: min(2048, 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.
+some space unavailable due to leaf headers. For example, a 4k sectorsize,
+maximum size of inline data is about 3900 bytes.
+
-Inlining can be completely turned off specifying 0. This will increase data
+Inlining can be completely turned off by specifying 0. This will increase data
block slack if file sizes are much smaller than block size but will reduce
metadata consumption in return.
++
+NOTE: the default value has changed to 2048 in kernel 4.6.
*metadata_ratio='value'*::
(default: 0, internal logic)
@@ -252,17 +275,27 @@ metadata consumption in return.
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
+there's not enough 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)
+(since: 3.2, default: off, deprecated since: 4.5)
+
-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.
+NOTE: this option has been replaced by 'usebackuproot' and should not be used
+but will work on 4.5+ kernels.
+
+*norecovery*::
+(since: 4.5, default: off)
++
+Do not attempt any data recovery at mount time. This will disable 'logreplay'
+and avoids other write operations.
++
+NOTE: The opposite option 'recovery' used to have different meaning but was
+changed for consistency with other filesystems, where 'norecovery' is used for
+skipping log replay. BTRFS does the same and in general will try to avoid any
+write operations.
*rescan_uuid_tree*::
(since: 3.12, default: off)
@@ -275,7 +308,7 @@ normally be needed.
+
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*.
+by *btrfs balance cancel*. The default behaviour is to start interrutpd balance.
*space_cache*::
*space_cache=v2*::
@@ -305,7 +338,8 @@ b-trees, sometimes referred to as 'free-space-tree'.
+
Options to control SSD allocation schemes. By default, BTRFS will
enable or disable SSD allocation heuristics depending on whether a
-rotational or non-rotational disk is in use. The 'ssd' and 'nossd' options
+rotational or non-rotational disk is in use (contents of
+'/sys/block/DEV/queue/rotational'). The 'ssd' and 'nossd' options
can override this autodetection.
+
The 'ssd_spread' mount option attempts to allocate into bigger and aligned
@@ -321,6 +355,9 @@ This mount option overrides the default subvolume set for the given filesystem.
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.
++
+NOTE: if both 'subvolid' and 'subvol' are specified, they must point at the
+same subvolume, otherwise mount will fail.
*subvolrootid='objectid'*::
(irrelevant since: 3.2, formally deprecated since: 3.10)
@@ -333,9 +370,9 @@ subvolume that did not reside directly under the toplevel subvolume.
+
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.
+processing data and metadata, higher numbers could lead to a performance hit
+due to increased locking contention, cache-line bouncing or costly data
+transfers between local CPU memories.
*treelog*::
*notreelog*::
@@ -346,11 +383,21 @@ 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!
+WARNING: currently, the tree log is replayed even with a read-only mount! To
+disable that behaviour, mount also with 'nologreplay'.
+
The tree log could contain new files/directories, these would not exist on
a mounted filesystem if the log is not replayed.
+*usebackuproot*::
+*nousebackuproot*::
++
+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.
++
+NOTE: This option has replaced 'recovery'.
+
*user_subvol_rm_allowed*::
(default: off)
+
diff --git a/Documentation/btrfs-receive.asciidoc b/Documentation/btrfs-receive.asciidoc
index 758eebe..e246603 100644
--- a/Documentation/btrfs-receive.asciidoc
+++ b/Documentation/btrfs-receive.asciidoc
@@ -3,51 +3,57 @@ btrfs-receive(8)
NAME
----
-btrfs-receive - receive subvolumes from stdin/file.
+btrfs-receive - receive subvolumes from send stream
SYNOPSIS
--------
-*btrfs receive* [options] <mount>
+*btrfs receive* [options] <path>
DESCRIPTION
-----------
-Receives one or more subvolumes that were previously
-sent with *btrfs send*. The received subvolumes are stored
-into <mount>.
-*btrfs receive* will fail with the following case:
+Receive a stream of changes and replicate one or more subvolumes that were
+previously used with *btrfs send* The received subvolumes are stored to
+'path'.
-1. a receiving subvolume already exists.
+*btrfs receive* will fail int the following cases:
-2. a previously received subvolume was changed after it was received.
+1. receiving subvolume already exists
-3. default subvolume is changed or you don't mount btrfs filesystem with
-fs tree.
+2. previously received subvolume was changed after it was received
-After receiving a subvolume, it is immediately set to read only.
+3. default subvolume has changed or you didn't mount BTRFS filesystem at the toplevel subvolume
+
+A subvolume is made read-only after the receiving process finishes succesfully.
`Options`
-v::
-Enable verbose debug output. Each occurrence of this option increases the
-verbose level more.
+enable verbose debug output, print each operation (each occurrence of this
+option increases the verbosity level)
+
-f <infile>::
-By default, btrfs receive uses stdin to receive the subvolumes.
-Use this option to specify a file to use instead.
+by default, btrfs receive uses standard input to receive the stream,
+use this option to read from a file instead
+
-C|--chroot::
-Confine the process to <mount> using chroot.
+confine the process to 'path' using `chroot`(1)
+
-e::
-Terminate after receiving an <end cmd> in the data stream.
-Without this option, the receiver terminates only if an error is recognized
-or on EOF.
+terminate after receiving an 'end cmd' marker in the stream.
++
+Without this option, the receiver terminates only if an error is encountered
+or at end of file
+
--max-errors <N>::
-Terminate as soon as N errors happened while processing commands from the send
-stream. Default value is 1. A value of 0 means no limit.
+terminate as soon as N errors happened while processing commands from the send
+stream, default value is 1, 0 means no limit
+
-m <mountpoint>::
-The root mount point of the destination fs.
+the root mount point of the destination filesystem
+
-By default the mountpoint is searched in /proc/self/mounts.
-If you do not have /proc, eg. in a chroot environment, use this option to tell
+By default the mountpoint is searched in '/proc/self/mounts'.
+If you do not have '/proc', eg. in a chroot environment, use this option to tell
us where this filesystem is mounted.
EXIT STATUS
diff --git a/Documentation/btrfs-restore.asciidoc b/Documentation/btrfs-restore.asciidoc
index ec3a08b..41e90e2 100644
--- a/Documentation/btrfs-restore.asciidoc
+++ b/Documentation/btrfs-restore.asciidoc
@@ -12,10 +12,22 @@ SYNOPSIS
DESCRIPTION
-----------
*btrfs restore* is used to try to salvage files from a damaged filesystem and
-restore them into <path> or just list the tree roots.
+restore them into <path> or just list the subvolume tree roots. The filesystem
+image is not modified.
-Since current `btrfs-check`(8) or `btrfs-rescue`(8) only has very limited usage,
-*btrfs restore* is normally a better choice.
+If the filesystem is damaged and cannot be repaired by the other tools
+(`btrfs-check`(8) or `btrfs-rescue`(8)), *btrfs restore* could be used to
+retrieve file data, as far as the metadata are readable. The checks done by
+restore are less strict and the process is usually able to get far enough to
+retrieve data from the whole filesystem. This comes at a cost that some data
+might be incomplete or from older versions if they're available.
+
+There are several options to attempt restoration of various file metadata type.
+You can try a dry run first to see how well the process goes and use further
+options to extend the set of restored metadata.
+
+For images with damaged tree structures, there are several options to point the
+process to some spare copy.
NOTE: It is recommended to read the following btrfs wiki page if your data is
not salvaged with default option: +
@@ -24,54 +36,59 @@ https://btrfs.wiki.kernel.org/index.php/Restore
OPTIONS
-------
-s|--snapshots::
-get snapshots, btrfs restore skips snapshots in default.
+get also snapshots that are skippped by default
-x|--xattr::
-get extended attributes.
+get extended attributes
-m|--metadata::
-restore owner, mode and times.
+restore owner, mode and times for files and directories
-S|--symlinks::
-restore symbolic links as well as normal files.
+restore symbolic links as well as normal files
-v|--verbose::
-verbose.
+be verbose and print what is being restored
-i|--ignore-errors::
-ignore errors.
+ignore errors during restoration and continue
-o|--overwrite::
-overwrite directories/files in <path>.
+overwrite directories/files in <path>, eg. for repeated runs
-t <bytenr>::
-use <bytenr> to read root tree.
+use <bytenr> to read the root tree
-f <bytenr>::
-only restore files that are under specified root whose root bytenr is <bytenr>.
+only restore files that are under specified subvolume root pointed by <bytenr>
-u|--super <mirror>::
-use given superblock mirror identified by <mirror>, it can be 0,1,2.
+use given superblock mirror identified by <mirror>, it can be 0,1 or 2
-r|--root <rootid>::
-only restore files that are under specified root whose objectid is <rootid>.
+only restore files that are under a specified subvolume whose objectid is <rootid>
-d::
-find dir.
+find directory
-l|--list-roots::
-list tree roots.
+list subvolume tree roots, can be used as argument for '-r'
-D|--dry-run::
-dry run (only list files that would be recovered).
+dry run (only list files that would be recovered)
--path-regex <regex>::
-restore only filenames matching regex, you have to use following syntax (possibly quoted):
+restore only filenames matching a regular expression (`regex`(7)) with a
+mandatory format
+
+^/(|home(|/username(|/Desktop(|/.*))))$+
++
+The format is not very comfortable and restores all files in the directories
+in the whole path, so this is not useful for restoring single file in a deep
+hierarchy.
-c::
-ignore case (--path-regex only).
+ignore case (--path-regex only)
EXIT STATUS
-----------
diff --git a/Documentation/btrfs-scrub.asciidoc b/Documentation/btrfs-scrub.asciidoc
index 2335aba..83ddcaa 100644
--- a/Documentation/btrfs-scrub.asciidoc
+++ b/Documentation/btrfs-scrub.asciidoc
@@ -3,7 +3,7 @@ btrfs-scrub(8)
NAME
----
-btrfs-scrub - scrub btrfs filesystem
+btrfs-scrub - scrub btrfs filesystem, verify block checksums
SYNOPSIS
--------
@@ -12,21 +12,22 @@ SYNOPSIS
DESCRIPTION
-----------
*btrfs scrub* is used to scrub a btrfs filesystem, which will read all data
-from all disks and verify checksums.
+and metadata blocks from all disks and verify checksums. Automatically repair
+corrupted blocks if there's a correct copy available.
SUBCOMMAND
----------
*cancel* <path>|<device>::
-If a scrub is running on the filesystem identified by <path>, cancel it.
+If a scrub is running on the filesystem identified by 'path>' cancel it.
+
-Progress is saved in the scrub progress file and scrubbing can be resumed later
-using the scrub resume command.
-If a <device> is given, the corresponding filesystem is found and
-scrub cancel behaves as if it was called on that filesystem.
+Progress is saved in the scrub progress file ('/var/lib/btrfs/scrub.status.UUID')
+and scrubbing can be resumed later using the *btrfs scrub resume* command.
+If a 'device' is specified, the corresponding filesystem is found and
+*btrfs scrub cancel* behaves as if it was called on that filesystem.
*resume* [-BdqrR] [-c <ioprio_class> -n <ioprio_classdata>] <path>|<device>::
-Resume a cancelled or interrupted scrub cycle on the filesystem identified by
-<path> or on a given <device>.
+Resume a cancelled or interrupted scrub on the filesystem identified by
+'path' or on a given 'device'.
+
Does not start a new scrub if the last scrub finished successfully.
+
@@ -35,11 +36,11 @@ Does not start a new scrub if the last scrub finished successfully.
see *scrub start*.
*start* [-BdqrRf] [-c <ioprio_class> -n <ioprio_classdata>] <path>|<device>::
-Start a scrub on all devices of the filesystem identified by <path> or on
-a single <device>. If a scrub is already running, the new one fails.
+Start a scrub on all devices of the filesystem identified by 'path' or on
+a single 'device'. If a scrub is already running, the new one fails.
+
Without options, scrub is started as a background process.
-Progress can be obtained with the *scrub status* command. Scrubbing
+Progress can be obtained with the *btrfs scrub status* command. Scrubbing
involves reading all data from all disks and verifying checksums. Errors are
corrected along the way if possible.
+
@@ -49,26 +50,28 @@ configured similar to the `ionice`(1) syntax using '-c' and '-n' options.
`Options`
+
-B::::
-Do not background and print scrub statistics when finished.
+do not background and print scrub statistics when finished
-d::::
-Print separate statistics for each device of the filesystem (-B only).
+print separate statistics for each device of the filesystem ('-B' only)
-q::::
-Quiet. Omit error messages and statistics.
+be quiet, omit error messages and statistics
-r::::
-Read only mode. Do not attempt to correct anything.
+read only mode, do not attempt to correct anything, can be run on a read-only
+filesystem
-R::::
-Raw print mode. Print full data instead of summary.
+print raw statistics per-device instead of a summary
-c <ioprio_class>::::
-Set IO priority class (see `ionice`(1) manpage).
+set IO priority class (see `ionice`(1) manpage)
-n <ioprio_classdata>::::
-Set IO priority classdata (see `ionice`(1) manpage).
+set IO priority classdata (see `ionice`(1) manpage)
-f::::
-Force starting new scrub even if a scrub is already running.
-This is useful when scrub status record file is damaged.
+force starting new scrub even if a scrub is already running,
+this is useful when scrub status file is damaged and reports a running
+scrub although it is not
*status* [-d] <path>|<device>::
-Show status of a running scrub for the filesystem identified by <path> or
-for the specified <device>.
+Show status of a running scrub for the filesystem identified by 'path' or
+for the specified 'device'.
+
If no scrub is running, show statistics of the last finished or cancelled scrub
for that filesystem or device.
@@ -76,7 +79,7 @@ for that filesystem or device.
`Options`
+
-d::::
-Print separate statistics for each device of the filesystem.
+print separate statistics for each device of the filesystem
EXIT STATUS
-----------
diff --git a/Documentation/btrfs-select-super.asciidoc b/Documentation/btrfs-select-super.asciidoc
index a8d7ef0..6e94a03 100644
--- a/Documentation/btrfs-select-super.asciidoc
+++ b/Documentation/btrfs-select-super.asciidoc
@@ -3,27 +3,45 @@ btrfs-select-super(8)
NAME
----
-btrfs-select-super - overwrite superblock with a backup
+btrfs-select-super - overwrite primary superblock with a backup copy
SYNOPSIS
--------
-*btrfs-select-super* -s number dev
+*btrfs-select-super* -s number <device>
DESCRIPTION
-----------
-*btrfs-select-super* destructively overwrites all copies of the superblock
-with a specified copy. This helps with certain cases of damage, especially
-when barriers were disabled during a power failure. You can find a valid
-copy of the superblock with *btrfs check -s*.
+Destructively overwrite all copies of the superblock
+with a specified copy. This helps in certain cases, for example when write
+barriers were disabled during a power failure and not all superblocks were
+written, or if the primary superblock is damaged, eg. accidentally overwritten.
-The filesystem specified by `dev` must not be mounted.
+The filesystem specified by 'device' must not be mounted.
+
+NOTE: *Prior to overwriting the primary superblock, please make sure that the backup
+copies are valid!*
+
+To dump a superblock use the *btrfs inspect-internal
+dump-super* command, or the obsolete command *btrfs-show-super*.
+
+Then run the check (in the non-repair mode) using the command *btrfs check -s*
+where '-s' specifies the superblock copy to use.
+
+Superblock copies exist in the following offsets on the device:
+
+- primary: '64KiB' (65536)
+- 1st copy: '64MiB' (67108864)
+- 2nd copy: '256GiB' (274877906944)
+
+A superblock size is '4KiB' (4096).
OPTIONS
-------
-s|--super <superblock>::
-use <superblock>th superblock copy, valid values are 0 up to 2 if the
-respective superblock offset is within the filesystem
+use 'superblock'th superblock copy, valid values are 0 1 or 2 if the
+respective superblock offset is within the device size
SEE ALSO
--------
+`btrfs-inspect-internal`(8),
`btrfsck check`(8)
diff --git a/Documentation/btrfs-send.asciidoc b/Documentation/btrfs-send.asciidoc
index e05342f..47b0b04 100644
--- a/Documentation/btrfs-send.asciidoc
+++ b/Documentation/btrfs-send.asciidoc
@@ -3,7 +3,7 @@ btrfs-send(8)
NAME
----
-btrfs-send - send data of subvolume(s) to stdout/file.
+btrfs-send - generate a stream of changes between two subvolumes
SYNOPSIS
--------
@@ -11,40 +11,53 @@ SYNOPSIS
DESCRIPTION
-----------
-Sends the subvolume(s) specified by <subvol> to stdout.
-<subvol> should be read-only here.
-By default, this will send the whole subvolume. To do an incremental
-send, use '-p <parent>'.
+This command will generate a stream of instructions that describe changes
+between two subvolumes. The stream can be consumed by the *btrfs receive*
+command to replicate the sent subvolume on a different filesystem.
+The command operates in two modes: full and incremental.
-If you want to allow btrfs to clone from any additional local snapshots,
-use '-c <clone-src>' (multiple times where applicable).
+All subvolumes involved in one send command must be read-only (ie. the
+read-only snapshots and this status cannot be changed if there's a running send
+operation that uses the subvolume).
-You must not specify clone sources unless you guarantee that these snapshots
-are exactly in the same state on both sides, the sender and the receiver.
+In the full mode, the entire subvolume data and metadata will end up in the
+stream.
+
+In the incremental mode (options '-p' and '-c'), there can be one or more
+parent subvolumes that will establish the base for determining the changes.
+The final stream will be smaller compared to the full mode.
It is allowed to omit the '-p <parent>' option when '-c <clone-src>' options
are given, in which case *btrfs send* will determine a suitable parent among the
clone sources itself.
+You must not specify clone sources unless you guarantee that these snapshots
+are exactly in the same state on both sides, the sender and the receiver.
+
`Options`
--v::
-Enable verbose debug output. Each occurrence of this option increases the
-verbose level more.
-e::
-If sending multiple subvols at once, use the new format and omit the <end cmd> between the subvols.
+if sending multiple subvolumes at once, use the new format and omit the
+'end cmd' marker in the stream separating the subvolumes
-p <parent>::
-Send an incremental stream from <parent> to <subvol>.
+send an incremental stream from 'parent' to 'subvol'
-c <clone-src>::
-Use this snapshot as a clone source for an incremental send (multiple allowed).
+use this snapshot as a clone source for an incremental send (multiple allowed)
-f <outfile>::
-Output is normally written to stdout. To write to a file, use this option.
-An alternative would be to use pipes.
+output is normally written to standard outout so it can be eg. piped to
+receive, use this option to write it to a file
--no-data::
-Send in NO_FILE_DATA mode. The output stream does not contain any file
+send in 'NO_FILE_DATA' mode
++
+The output stream does not contain any file
data and thus cannot be used to transfer changes. This mode is faster and
useful to show the differences in metadata.
+-v|--verbose::
+enable verbose output, print generated commands in a readable form, (each
+occurrence of this option increases the verbosity level)
+-q|--quiet::
+suppress all messagese except errors
EXIT STATUS
-----------
diff --git a/Documentation/btrfs-subvolume.asciidoc b/Documentation/btrfs-subvolume.asciidoc
index 5497c57..fb64aa4 100644
--- a/Documentation/btrfs-subvolume.asciidoc
+++ b/Documentation/btrfs-subvolume.asciidoc
@@ -46,7 +46,7 @@ SUBCOMMAND
*create* [-i <qgroupid>] [<dest>]<name>::
Create a subvolume <name> in <dest>.
+
-If <dest> is not given, subvolume <name> will be created in the currently
+If <dest> is not given, subvolume <name> will be created in the current
directory.
+
`Options`
diff --git a/Documentation/mkfs.btrfs.asciidoc b/Documentation/mkfs.btrfs.asciidoc
index e4321de..5d79e24 100644
--- a/Documentation/mkfs.btrfs.asciidoc
+++ b/Documentation/mkfs.btrfs.asciidoc
@@ -275,14 +275,27 @@ 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 redundancy and just
-wastes space.
+wastes filesystem space without the expected level of redundancy.
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.
+details are not usually disclosed by vendors. For example we could expect that
+not all blocks get deduplicated. This will provide a non-zero probability of
+recovery compared to a zero chance if the single profile is used. The user
+should make the tradeoff decision. The deduplication in SSDs is thought to be
+widely available so the reason behind the mkfs default is to not give a false
+sense of redundancy.
+
+As another example, the widely used USB flash or SD cards use a translation
+layer between the logical and physical view of the device. The data lifetime
+may be affected by frequent plugging. The memory cells could get damaged,
+hopefully not destroying both copies of particular data in case of DUP.
+
+The wear levelling techniques can also lead to reduced redundancy, even if the
+device does not do any deduplication. The controllers may put data written in
+a short timespan into the same physical storage unit (cell, block etc). In case
+this unit dies, both copies are lost. BTRFS does not add any artificial delay
+between metadata writes.
The traditional rotational hard drives usually fail at the sector level.
diff --git a/Makefile.in b/Makefile.in
index 0a1aece..ac6b353 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -6,11 +6,14 @@
# test run the full testsuite
# install install to default location (/usr/local)
# clean clean built binaries (not the documentation)
+# clean-all clean as above, clean docs and generated files
#
# Tuning by variables (environment or make arguments):
# V=1 verbose, print command lines (default: quiet)
# C=1 run checker before compilation (default checker: sparse)
+# D=1 debugging build, turn off optimizations
# W=123 build with warnings (default: off)
+# DEBUG_CFLAGS additional compiler flags for debugging build
# EXTRA_CFLAGS additional compiler flags
# EXTRA_LDFLAGS additional linker flags
#
@@ -36,13 +39,20 @@ DISABLE_BTRFSCONVERT = @DISABLE_BTRFSCONVERT@
EXTRA_CFLAGS :=
EXTRA_LDFLAGS :=
+DEBUG_CFLAGS_DEFAULT = -O0 -U_FORTIFY_SOURCE -ggdb3
+DEBUG_CFLAGS_INTERNAL =
+DEBUG_CFLAGS :=
+
# Common build flags
CFLAGS = @CFLAGS@ \
-include config.h \
-DBTRFS_FLAT_INCLUDES \
-D_XOPEN_SOURCE=700 \
-fno-strict-aliasing \
- -fPIC $(EXTRAWARN_CFLAGS) $(EXTRA_CFLAGS)
+ -fPIC \
+ $(EXTRAWARN_CFLAGS) \
+ $(DEBUG_CFLAGS_INTERNAL) \
+ $(EXTRA_CFLAGS)
LDFLAGS = @LDFLAGS@ \
-rdynamic $(EXTRA_LDFLAGS)
@@ -84,11 +94,15 @@ libbtrfs_headers = send-stream.h send-utils.h send.h rbtree.h btrfs-list.h \
extent_io.h ioctl.h ctree.h btrfsck.h version.h
TESTS = fsck-tests.sh convert-tests.sh
+udev_rules = 64-btrfs-dm.rules
+
prefix ?= @prefix@
exec_prefix = @exec_prefix@
bindir = @bindir@
libdir ?= @libdir@
incdir = @includedir@/btrfs
+udevdir = @UDEVDIR@
+udevruledir = ${udevdir}/rules.d
ifeq ("$(origin V)", "command line")
BUILD_VERBOSE = $(V)
@@ -103,6 +117,10 @@ else
Q = @
endif
+ifeq ("$(origin D)", "command line")
+ DEBUG_CFLAGS_INTERNAL = $(DEBUG_CFLAGS_DEFAULT) $(DEBUG_CFLAGS)
+endif
+
MAKEOPTS = --no-print-directory Q=$(Q)
# build all by default
@@ -224,6 +242,12 @@ test-clean:
@echo "Cleaning tests"
$(Q)bash tests/clean-tests.sh
+test-inst: all
+ @tmpdest=`mktemp --tmpdir -d btrfs-inst.XXXXXX` && \
+ echo "Test installation to $$tmpdest" && \
+ $(MAKE) DESTDIR=$$tmpdest install && \
+ $(RM) -rf -- $$tmpdest
+
test: test-fsck test-mkfs test-convert test-misc test-fuzz
#
@@ -390,6 +414,10 @@ install: $(libs) $(progs_install) $(INSTALLDIRS)
cp -a $(lib_links) $(DESTDIR)$(libdir)
$(INSTALL) -m755 -d $(DESTDIR)$(incdir)
$(INSTALL) -m644 $(headers) $(DESTDIR)$(incdir)
+ifneq ($(udevdir),)
+ $(INSTALL) -m755 -d $(DESTDIR)$(udevruledir)
+ $(INSTALL) -m644 $(udev_rules) $(DESTDIR)$(udevruledir)
+endif
install-static: $(progs_static) $(INSTALLDIRS)
$(INSTALL) -m755 -d $(DESTDIR)$(bindir)
diff --git a/autogen.sh b/autogen.sh
index 9669850..80442c5 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -64,9 +64,10 @@ echo " automake: $(automake --version | head -1)"
chmod +x version.sh
rm -rf autom4te.cache
-aclocal $AL_OPTS
-autoconf $AC_OPTS
-autoheader $AH_OPTS
+aclocal -I m4 $AL_OPTS &&
+autoconf -I m4 $AC_OPTS &&
+autoheader -I m4 $AH_OPTS ||
+exit 1
# it's better to use helper files from automake installation than
# maintain copies in git tree
@@ -92,7 +93,7 @@ find_autofile config.guess
find_autofile config.sub
find_autofile install-sh
-cd $THEDIR
+cd "$THEDIR"
echo
echo "Now type '$srcdir/configure' and 'make' to compile."
diff --git a/backref.c b/backref.c
index 9d48a10..7b3b592 100644
--- a/backref.c
+++ b/backref.c
@@ -1578,8 +1578,8 @@ static int inode_to_path(u64 inum, u32 name_len, unsigned long name_off,
* is has been created large enough. each path is zero-terminated and accessed
* from ipath->fspath->val[i].
* when it returns, there are ipath->fspath->elem_cnt number of paths available
- * in ipath->fspath->val[]. when the allocated space wasn't sufficient, the
- * number of missed paths in recored in ipath->fspath->elem_missed, otherwise,
+ * in ipath->fspath->val[]. When the allocated space wasn't sufficient, the
+ * number of missed paths is recorded in ipath->fspath->elem_missed, otherwise,
* it's zero. ipath->fspath->bytes_missing holds the number of bytes that would
* have been needed to return all paths.
*/
diff --git a/btrfs-convert.c b/btrfs-convert.c
index b49775c..b18de59 100644
--- a/btrfs-convert.c
+++ b/btrfs-convert.c
@@ -44,6 +44,18 @@
#define INO_OFFSET (BTRFS_FIRST_FREE_OBJECTID - EXT2_ROOT_INO)
#define CONV_IMAGE_SUBVOL_OBJECTID BTRFS_FIRST_FREE_OBJECTID
+/*
+ * Compatibility code for e2fsprogs 1.41 which doesn't support RO compat flag
+ * BIGALLOC.
+ * Unlike normal RO compat flag, BIGALLOC affects how e2fsprogs check used
+ * space, and btrfs-convert heavily relies on it.
+ */
+#ifdef HAVE_OLD_E2FSPROGS
+#define EXT2FS_CLUSTER_RATIO(fs) (1)
+#define EXT2_CLUSTERS_PER_GROUP(s) (EXT2_BLOCKS_PER_GROUP(s))
+#define EXT2FS_B2C(fs, blk) (blk)
+#endif
+
struct task_ctx {
uint32_t max_copy_inodes;
uint32_t cur_copy_inodes;
@@ -81,42 +93,13 @@ 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 (*read_used_space)(struct btrfs_convert_context *cctx);
int (*copy_inodes)(struct btrfs_convert_context *cctx,
struct btrfs_root *root, int datacsum,
int packing, int noxattr, struct task_ctx *p);
void (*close_fs)(struct btrfs_convert_context *cctx);
};
-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);
@@ -131,36 +114,6 @@ static void clean_convert_context(struct btrfs_convert_context *cctx)
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)
@@ -183,9 +136,23 @@ static int ext2_open_fs(struct btrfs_convert_context *cctx, const char *name)
errcode_t ret;
ext2_filsys ext2_fs;
ext2_ino_t ino;
+ u32 ro_feature;
+
ret = ext2fs_open(name, 0, 0, 0, unix_io_manager, &ext2_fs);
if (ret) {
fprintf(stderr, "ext2fs_open: %s\n", error_message(ret));
+ return -1;
+ }
+ /*
+ * We need to know exactly the used space, some RO compat flags like
+ * BIGALLOC will affect how used space is present.
+ * So we need manuall check any unsupported RO compat flags
+ */
+ ro_feature = ext2_fs->super->s_feature_ro_compat;
+ if (ro_feature & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP) {
+ error(
+"unsupported RO features detected: %x, abort convert to avoid possible corruption",
+ ro_feature & ~EXT2_LIB_FEATURE_COMPAT_SUPP);
goto fail;
}
ret = ext2fs_read_inode_bitmap(ext2_fs);
@@ -227,162 +194,84 @@ static int ext2_open_fs(struct btrfs_convert_context *cctx, const char *name)
cctx->free_inodes_count = ext2_fs->super->s_free_inodes_count;
return 0;
fail:
+ ext2fs_close(ext2_fs);
return -1;
}
-static void ext2_close_fs(struct btrfs_convert_context *cctx)
-{
- if (cctx->volume_name) {
- free(cctx->volume_name);
- cctx->volume_name = NULL;
- }
- ext2fs_close(cctx->fs_data);
-}
-
-static int ext2_alloc_block(struct btrfs_convert_context *cctx,
- u64 goal, u64 *block_ret)
+static int __ext2_add_one_block(ext2_filsys fs, char *bitmap,
+ unsigned long group_nr, struct cache_tree *used)
{
- ext2_filsys fs = cctx->fs_data;
- blk_t block;
-
- if (!ext2fs_new_block(fs, goal, NULL, &block)) {
- ext2fs_fast_mark_block_bitmap(fs->block_map, block);
- *block_ret = block;
- return 0;
- }
- return -ENOSPC;
-}
+ unsigned long offset;
+ unsigned i;
+ int ret = 0;
-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);
- blk_t end = ext2fs_get_block_bitmap_end(bitmap);
-
- for (block = max_t(u64, goal, start); block + num < end; block++) {
- if (ext2fs_fast_test_block_bitmap_range(bitmap, block, num)) {
- ext2fs_fast_mark_block_bitmap_range(bitmap, block,
- num);
- *block_ret = block;
- return 0;
+ offset = fs->super->s_first_data_block;
+ offset /= EXT2FS_CLUSTER_RATIO(fs);
+ offset += group_nr * EXT2_CLUSTERS_PER_GROUP(fs->super);
+ for (i = 0; i < EXT2_CLUSTERS_PER_GROUP(fs->super); i++) {
+ if (ext2fs_test_bit(i, bitmap)) {
+ u64 start;
+
+ start = (i + offset) * EXT2FS_CLUSTER_RATIO(fs);
+ start *= fs->blocksize;
+ ret = add_merge_cache_extent(used, start,
+ fs->blocksize);
+ if (ret < 0)
+ break;
}
}
- return -ENOSPC;
-}
-
-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);
-}
-
-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 ret;
}
-static int cache_free_extents(struct btrfs_root *root,
- struct btrfs_convert_context *cctx)
+/*
+ * Read all used ext2 space into cctx->used cache tree
+ */
+static int ext2_read_used_space(struct btrfs_convert_context *cctx)
+{
+ ext2_filsys fs = (ext2_filsys)cctx->fs_data;
+ blk64_t blk_itr = EXT2FS_B2C(fs, fs->super->s_first_data_block);
+ struct cache_tree *used_tree = &cctx->used;
+ char *block_bitmap = NULL;
+ unsigned long i;
+ int block_nbytes;
+ int ret = 0;
-{
- int i, ret = 0;
- blk_t block;
- u64 bytenr;
- u64 blocksize = cctx->blocksize;
+ block_nbytes = EXT2_CLUSTERS_PER_GROUP(fs->super) / 8;
+ /* Shouldn't happen */
+ BUG_ON(!fs->block_map);
- 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,
- bytenr, bytenr + blocksize - 1, 0);
- BUG_ON(ret);
- }
+ block_bitmap = malloc(block_nbytes);
+ if (!block_bitmap)
+ return -ENOMEM;
- for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) {
- bytenr = btrfs_sb_offset(i);
- bytenr &= ~((u64)BTRFS_STRIPE_LEN - 1);
- if (bytenr >= blocksize * cctx->block_count)
+ for (i = 0; i < fs->group_desc_count; i++) {
+ ret = ext2fs_get_block_bitmap_range(fs->block_map, blk_itr,
+ block_nbytes * 8, block_bitmap);
+ if (ret) {
+ error("fail to get bitmap from ext2, %s",
+ strerror(-ret));
break;
- clear_extent_dirty(&root->fs_info->free_space_cache, bytenr,
- bytenr + BTRFS_STRIPE_LEN - 1, 0);
+ }
+ ret = __ext2_add_one_block(fs, block_bitmap, i, used_tree);
+ if (ret < 0) {
+ error("fail to build used space tree, %s",
+ strerror(-ret));
+ break;
+ }
+ blk_itr += EXT2_CLUSTERS_PER_GROUP(fs->super);
}
- clear_extent_dirty(&root->fs_info->free_space_cache,
- 0, BTRFS_SUPER_INFO_OFFSET - 1, 0);
-
- return 0;
+ free(block_bitmap);
+ return ret;
}
-static int custom_alloc_extent(struct btrfs_root *root, u64 num_bytes,
- u64 hint_byte, struct btrfs_key *ins,
- int metadata)
+static void ext2_close_fs(struct btrfs_convert_context *cctx)
{
- u64 start;
- u64 end;
- u64 last = hint_byte;
- int ret;
- int wrapped = 0;
- struct btrfs_block_group_cache *cache;
-
- while(1) {
- ret = find_first_extent_bit(&root->fs_info->free_space_cache,
- last, &start, &end, EXTENT_DIRTY);
- if (ret) {
- if (wrapped++ == 0) {
- last = 0;
- continue;
- } else {
- goto fail;
- }
- }
-
- start = max(last, start);
- last = end + 1;
- if (last - start < num_bytes)
- continue;
-
- last = start + num_bytes;
- if (test_range_bit(&root->fs_info->pinned_extents,
- start, last - 1, EXTENT_DIRTY, 0))
- continue;
-
- cache = btrfs_lookup_block_group(root->fs_info, start);
- BUG_ON(!cache);
- if (cache->flags & BTRFS_BLOCK_GROUP_SYSTEM ||
- last > cache->key.objectid + cache->key.offset) {
- last = cache->key.objectid + cache->key.offset;
- continue;
- }
-
- if (metadata) {
- BUG_ON(num_bytes != root->nodesize);
- if (check_crossing_stripes(start, num_bytes)) {
- last = round_down(start + num_bytes,
- BTRFS_STRIPE_LEN);
- continue;
- }
- }
- clear_extent_dirty(&root->fs_info->free_space_cache,
- start, start + num_bytes - 1, 0);
-
- ins->objectid = start;
- ins->offset = num_bytes;
- ins->type = BTRFS_EXTENT_ITEM_KEY;
- return 0;
+ if (cctx->volume_name) {
+ free(cctx->volume_name);
+ cctx->volume_name = NULL;
}
-fail:
- fprintf(stderr, "not enough free space\n");
- return -ENOSPC;
+ ext2fs_close(cctx->fs_data);
}
static int intersect_with_sb(u64 bytenr, u64 num_bytes)
@@ -401,17 +290,6 @@ static int intersect_with_sb(u64 bytenr, u64 num_bytes)
return 0;
}
-static int custom_free_extent(struct btrfs_root *root, u64 bytenr,
- u64 num_bytes)
-{
- return intersect_with_sb(bytenr, num_bytes);
-}
-
-static struct btrfs_extent_ops extent_ops = {
- .alloc_extent = custom_alloc_extent,
- .free_extent = custom_free_extent,
-};
-
static int convert_insert_dirent(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
const char *name, size_t name_len,
@@ -583,7 +461,9 @@ static int csum_disk_extent(struct btrfs_trans_handle *trans,
struct blk_iterate_data {
struct btrfs_trans_handle *trans;
struct btrfs_root *root;
+ struct btrfs_root *convert_root;
struct btrfs_inode_item *inode;
+ u64 convert_ino;
u64 objectid;
u64 first_block;
u64 disk_block;
@@ -599,6 +479,8 @@ static void init_blk_iterate_data(struct blk_iterate_data *data,
struct btrfs_inode_item *inode,
u64 objectid, int checksum)
{
+ struct btrfs_key key;
+
data->trans = trans;
data->root = root;
data->inode = inode;
@@ -609,25 +491,110 @@ static void init_blk_iterate_data(struct blk_iterate_data *data,
data->boundary = (u64)-1;
data->checksum = checksum;
data->errcode = 0;
+
+ key.objectid = CONV_IMAGE_SUBVOL_OBJECTID;
+ key.type = BTRFS_ROOT_ITEM_KEY;
+ key.offset = (u64)-1;
+ data->convert_root = btrfs_read_fs_root(root->fs_info, &key);
+ /* Impossible as we just opened it before */
+ BUG_ON(!data->convert_root || IS_ERR(data->convert_root));
+ data->convert_ino = BTRFS_FIRST_FREE_OBJECTID + 1;
}
+/*
+ * Record a file extent in original filesystem into btrfs one.
+ * The special point is, old disk_block can point to a reserved range.
+ * So here, we don't use disk_block directly but search convert_root
+ * to get the real disk_bytenr.
+ */
static int record_file_blocks(struct blk_iterate_data *data,
u64 file_block, u64 disk_block, u64 num_blocks)
{
- int ret;
+ int ret = 0;
struct btrfs_root *root = data->root;
+ struct btrfs_root *convert_root = data->convert_root;
+ struct btrfs_path *path;
u64 file_pos = file_block * root->sectorsize;
- u64 disk_bytenr = disk_block * root->sectorsize;
+ u64 old_disk_bytenr = disk_block * root->sectorsize;
u64 num_bytes = num_blocks * root->sectorsize;
- ret = btrfs_record_file_extent(data->trans, data->root,
- data->objectid, data->inode, file_pos,
- disk_bytenr, num_bytes);
-
- if (ret || !data->checksum || disk_bytenr == 0)
- return ret;
+ u64 cur_off = old_disk_bytenr;
- return csum_disk_extent(data->trans, data->root, disk_bytenr,
+ /* Hole, pass it to record_file_extent directly */
+ if (old_disk_bytenr == 0)
+ return btrfs_record_file_extent(data->trans, root,
+ data->objectid, data->inode, file_pos, 0,
num_bytes);
+
+ path = btrfs_alloc_path();
+ if (!path)
+ return -ENOMEM;
+
+ /*
+ * Search real disk bytenr from convert root
+ */
+ while (cur_off < old_disk_bytenr + num_bytes) {
+ struct btrfs_key key;
+ struct btrfs_file_extent_item *fi;
+ struct extent_buffer *node;
+ int slot;
+ u64 extent_disk_bytenr;
+ u64 extent_num_bytes;
+ u64 real_disk_bytenr;
+ u64 cur_len;
+
+ key.objectid = data->convert_ino;
+ key.type = BTRFS_EXTENT_DATA_KEY;
+ key.offset = cur_off;
+
+ ret = btrfs_search_slot(NULL, convert_root, &key, path, 0, 0);
+ if (ret < 0)
+ break;
+ if (ret > 0) {
+ ret = btrfs_previous_item(convert_root, path,
+ data->convert_ino,
+ BTRFS_EXTENT_DATA_KEY);
+ if (ret < 0)
+ break;
+ if (ret > 0) {
+ ret = -ENOENT;
+ break;
+ }
+ }
+ node = path->nodes[0];
+ slot = path->slots[0];
+ btrfs_item_key_to_cpu(node, &key, slot);
+ BUG_ON(key.type != BTRFS_EXTENT_DATA_KEY ||
+ key.objectid != data->convert_ino ||
+ key.offset > cur_off);
+ fi = btrfs_item_ptr(node, slot, struct btrfs_file_extent_item);
+ extent_disk_bytenr = btrfs_file_extent_disk_bytenr(node, fi);
+ extent_num_bytes = btrfs_file_extent_disk_num_bytes(node, fi);
+ BUG_ON(cur_off - key.offset >= extent_num_bytes);
+ btrfs_release_path(path);
+
+ if (extent_disk_bytenr)
+ real_disk_bytenr = cur_off - key.offset +
+ extent_disk_bytenr;
+ else
+ real_disk_bytenr = 0;
+ cur_len = min(key.offset + extent_num_bytes,
+ old_disk_bytenr + num_bytes) - cur_off;
+ ret = btrfs_record_file_extent(data->trans, data->root,
+ data->objectid, data->inode, file_pos,
+ real_disk_bytenr, cur_len);
+ if (ret < 0)
+ break;
+ cur_off += cur_len;
+ file_pos += cur_len;
+
+ /*
+ * No need to care about csum
+ * As every byte of old fs image is calculated for csum, no
+ * need to waste CPU cycles now.
+ */
+ }
+ btrfs_free_path(path);
+ return ret;
}
static int block_iterate_proc(u64 disk_block, u64 file_block,
@@ -1230,29 +1197,6 @@ static int copy_single_inode(struct btrfs_trans_handle *trans,
return btrfs_insert_inode(trans, root, objectid, &btrfs_inode);
}
-static int copy_disk_extent(struct btrfs_root *root, u64 dst_bytenr,
- u64 src_bytenr, u32 num_bytes)
-{
- int ret;
- char *buffer;
- struct btrfs_fs_devices *fs_devs = root->fs_info->fs_devices;
-
- buffer = malloc(num_bytes);
- if (!buffer)
- return -ENOMEM;
- ret = pread(fs_devs->latest_bdev, buffer, num_bytes, src_bytenr);
- if (ret != num_bytes)
- goto fail;
- ret = pwrite(fs_devs->latest_bdev, buffer, num_bytes, dst_bytenr);
- if (ret != num_bytes)
- goto fail;
- ret = 0;
-fail:
- free(buffer);
- if (ret > 0)
- ret = -1;
- return ret;
-}
/*
* scan ext2's inode bitmap and copy all used inodes.
*/
@@ -1312,250 +1256,365 @@ static int ext2_copy_inodes(struct btrfs_convert_context *cctx,
return ret;
}
-static int ext2_test_block(struct btrfs_convert_context *cctx, u64 block)
-{
- ext2_filsys ext2_fs = cctx->fs_data;
+static int create_image_file_range(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct cache_tree *used,
+ struct btrfs_inode_item *inode,
+ u64 ino, u64 bytenr, u64 *ret_len,
+ int datacsum)
+{
+ struct cache_extent *cache;
+ struct btrfs_block_group_cache *bg_cache;
+ u64 len = *ret_len;
+ u64 disk_bytenr;
+ int i;
+ int ret;
+
+ BUG_ON(bytenr != round_down(bytenr, root->sectorsize));
+ BUG_ON(len != round_down(len, root->sectorsize));
+ len = min_t(u64, len, BTRFS_MAX_EXTENT_SIZE);
+
+ /*
+ * Skip sb ranges first
+ * [0, 1M), [sb_offset(1), +64K), [sb_offset(2), +64K].
+ *
+ * Or we will insert a hole into current image file, and later
+ * migrate block will fail as there is already a file extent.
+ */
+ if (bytenr < 1024 * 1024) {
+ *ret_len = 1024 * 1024 - bytenr;
+ return 0;
+ }
+ for (i = 1; i < BTRFS_SUPER_MIRROR_MAX; i++) {
+ u64 cur = btrfs_sb_offset(i);
+
+ if (bytenr >= cur && bytenr < cur + BTRFS_STRIPE_LEN) {
+ *ret_len = cur + BTRFS_STRIPE_LEN - bytenr;
+ return 0;
+ }
+ }
+ for (i = 1; i < BTRFS_SUPER_MIRROR_MAX; i++) {
+ u64 cur = btrfs_sb_offset(i);
+
+ /*
+ * |--reserved--|
+ * |----range-------|
+ * May still need to go through file extent inserts
+ */
+ if (bytenr < cur && bytenr + len >= cur) {
+ len = min_t(u64, len, cur - bytenr);
+ break;
+ }
+ /*
+ * |--reserved--|
+ * |---range---|
+ * Drop out, no need to insert anything
+ */
+ if (bytenr >= cur && bytenr < cur + BTRFS_STRIPE_LEN) {
+ *ret_len = cur + BTRFS_STRIPE_LEN - bytenr;
+ return 0;
+ }
+ }
+
+ cache = search_cache_extent(used, bytenr);
+ if (cache) {
+ if (cache->start <= bytenr) {
+ /*
+ * |///////Used///////|
+ * |<--insert--->|
+ * bytenr
+ */
+ len = min_t(u64, len, cache->start + cache->size -
+ bytenr);
+ disk_bytenr = bytenr;
+ } else {
+ /*
+ * |//Used//|
+ * |<-insert-->|
+ * bytenr
+ */
+ len = min(len, cache->start - bytenr);
+ disk_bytenr = 0;
+ datacsum = 0;
+ }
+ } else {
+ /*
+ * |//Used//| |EOF
+ * |<-insert-->|
+ * bytenr
+ */
+ disk_bytenr = 0;
+ datacsum = 0;
+ }
+
+ if (disk_bytenr) {
+ /* Check if the range is in a data block group */
+ bg_cache = btrfs_lookup_block_group(root->fs_info, bytenr);
+ if (!bg_cache)
+ return -ENOENT;
+ if (!(bg_cache->flags & BTRFS_BLOCK_GROUP_DATA))
+ return -EINVAL;
+
+ /* The extent should never cross block group boundary */
+ len = min_t(u64, len, bg_cache->key.objectid +
+ bg_cache->key.offset - bytenr);
+ }
+
+ BUG_ON(len != round_down(len, root->sectorsize));
+ ret = btrfs_record_file_extent(trans, root, ino, inode, bytenr,
+ disk_bytenr, len);
+ if (ret < 0)
+ return ret;
- BUG_ON(block != (u32)block);
- return ext2fs_fast_test_block_bitmap(ext2_fs->block_map, block);
+ if (datacsum)
+ ret = csum_disk_extent(trans, root, bytenr, len);
+ *ret_len = len;
+ return ret;
}
+
/*
- * Construct a range of ext2fs image file.
- * scan block allocation bitmap, find all blocks used by the ext2fs
- * in this range and create file extents that point to these blocks.
+ * Relocate old fs data in one reserved ranges
*
- * Note: Before calling the function, no file extent points to blocks
- * in this range
+ * Since all old fs data in reserved range is not covered by any chunk nor
+ * data extent, we don't need to handle any reference but add new
+ * extent/reference, which makes codes more clear
*/
-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,
- struct btrfs_convert_context *cctx, int datacsum)
-{
- u32 blocksize = cctx->blocksize;
- u32 block = start_byte / blocksize;
- u32 last_block = (end_byte + blocksize - 1) / blocksize;
+static int migrate_one_reserved_range(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct cache_tree *used,
+ struct btrfs_inode_item *inode, int fd,
+ u64 ino, u64 start, u64 len, int datacsum)
+{
+ u64 cur_off = start;
+ u64 cur_len = len;
+ u64 hole_start = start;
+ u64 hole_len;
+ struct cache_extent *cache;
+ struct btrfs_key key;
+ struct extent_buffer *eb;
int ret = 0;
- struct blk_iterate_data data;
- init_blk_iterate_data(&data, trans, root, inode, objectid, datacsum);
- data.first_block = block;
+ while (cur_off < start + len) {
+ cache = lookup_cache_extent(used, cur_off, cur_len);
+ if (!cache)
+ break;
+ cur_off = max(cache->start, cur_off);
+ cur_len = min(cache->start + cache->size, start + len) -
+ cur_off;
+ BUG_ON(cur_len < root->sectorsize);
+
+ /* reserve extent for the data */
+ ret = btrfs_reserve_extent(trans, root, cur_len, 0, 0, (u64)-1,
+ &key, 1);
+ if (ret < 0)
+ break;
+
+ eb = malloc(sizeof(*eb) + cur_len);
+ if (!eb) {
+ ret = -ENOMEM;
+ break;
+ }
- for (; start_byte < end_byte; block++, start_byte += blocksize) {
- if (!convert_test_block(cctx, block))
- continue;
- ret = block_iterate_proc(block, block, &data);
+ ret = pread(fd, eb->data, cur_len, cur_off);
+ if (ret < cur_len) {
+ ret = (ret < 0 ? ret : -EIO);
+ free(eb);
+ break;
+ }
+ eb->start = key.objectid;
+ eb->len = key.offset;
+
+ /* Write the data */
+ ret = write_and_map_eb(trans, root, eb);
+ free(eb);
if (ret < 0)
- goto fail;
- }
- if (data.num_blocks > 0) {
- ret = record_file_blocks(&data, data.first_block,
- data.disk_block, data.num_blocks);
- if (ret)
- goto fail;
- data.first_block += data.num_blocks;
- }
- if (last_block > data.first_block) {
- ret = record_file_blocks(&data, data.first_block, 0,
- last_block - data.first_block);
- if (ret)
- goto fail;
+ break;
+
+ /* Now handle extent item and file extent things */
+ ret = btrfs_record_file_extent(trans, root, ino, inode, cur_off,
+ key.objectid, key.offset);
+ if (ret < 0)
+ break;
+ /* Finally, insert csum items */
+ if (datacsum)
+ ret = csum_disk_extent(trans, root, key.objectid,
+ key.offset);
+
+ /* Don't forget to insert hole */
+ hole_len = cur_off - hole_start;
+ if (hole_len) {
+ ret = btrfs_record_file_extent(trans, root, ino, inode,
+ hole_start, 0, hole_len);
+ if (ret < 0)
+ break;
+ }
+
+ cur_off += key.offset;
+ hole_start = cur_off;
+ cur_len = start + len - cur_off;
}
-fail:
+ /* Last hole */
+ if (start + len - hole_start > 0)
+ ret = btrfs_record_file_extent(trans, root, ino, inode,
+ hole_start, 0, start + len - hole_start);
+ return ret;
+}
+
+/*
+ * Relocate the used ext2 data in reserved ranges
+ * [0,1M)
+ * [btrfs_sb_offset(1), +BTRFS_STRIPE_LEN)
+ * [btrfs_sb_offset(2), +BTRFS_STRIPE_LEN)
+ */
+static int migrate_reserved_ranges(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct cache_tree *used,
+ struct btrfs_inode_item *inode, int fd,
+ u64 ino, u64 total_bytes, int datacsum)
+{
+ u64 cur_off;
+ u64 cur_len;
+ int ret = 0;
+
+ /* 0 ~ 1M */
+ cur_off = 0;
+ cur_len = 1024 * 1024;
+ ret = migrate_one_reserved_range(trans, root, used, inode, fd, ino,
+ cur_off, cur_len, datacsum);
+ if (ret < 0)
+ return ret;
+
+ /* second sb(fisrt sb is included in 0~1M) */
+ cur_off = btrfs_sb_offset(1);
+ cur_len = min(total_bytes, cur_off + BTRFS_STRIPE_LEN) - cur_off;
+ if (cur_off > total_bytes)
+ return ret;
+ ret = migrate_one_reserved_range(trans, root, used, inode, fd, ino,
+ cur_off, cur_len, datacsum);
+ if (ret < 0)
+ return ret;
+
+ /* Last sb */
+ cur_off = btrfs_sb_offset(2);
+ cur_len = min(total_bytes, cur_off + BTRFS_STRIPE_LEN) - cur_off;
+ if (cur_off > total_bytes)
+ return ret;
+ ret = migrate_one_reserved_range(trans, root, used, inode, fd, ino,
+ cur_off, cur_len, datacsum);
return ret;
}
+
+static int wipe_reserved_ranges(struct cache_tree *tree, u64 min_stripe_size,
+ int ensure_size);
+
/*
- * Create the fs image file.
+ * Create the fs image file of old filesystem.
+ *
+ * This is completely fs independent as we have cctx->used, only
+ * need to create file extents pointing to all the positions.
*/
-static int create_image(struct btrfs_convert_context *cctx,
- struct btrfs_root *root, const char *name, int datacsum)
+static int create_image(struct btrfs_root *root,
+ struct btrfs_mkfs_config *cfg,
+ struct btrfs_convert_context *cctx, int fd,
+ u64 size, char *name, int datacsum)
{
- int ret;
- struct btrfs_key key;
- struct btrfs_key location;
- struct btrfs_path path;
- struct btrfs_inode_item btrfs_inode;
- struct btrfs_inode_item *inode_item;
- struct extent_buffer *leaf;
- struct btrfs_fs_info *fs_info = root->fs_info;
- struct btrfs_root *extent_root = fs_info->extent_root;
+ struct btrfs_inode_item buf;
struct btrfs_trans_handle *trans;
- struct btrfs_extent_item *ei;
- struct btrfs_extent_inline_ref *iref;
- struct btrfs_extent_data_ref *dref;
- u64 bytenr;
- u64 num_bytes;
- u64 objectid;
- u64 last_byte;
- u64 first_free;
- u64 total_bytes;
- u64 flags = BTRFS_INODE_READONLY;
- u32 sectorsize = root->sectorsize;
+ struct btrfs_path *path = NULL;
+ struct btrfs_key key;
+ struct cache_extent *cache;
+ struct cache_tree used_tmp;
+ u64 cur;
+ u64 ino;
+ int ret;
- total_bytes = btrfs_super_total_bytes(fs_info->super_copy);
- first_free = BTRFS_SUPER_INFO_OFFSET + sectorsize * 2 - 1;
- first_free &= ~((u64)sectorsize - 1);
- if (!datacsum)
- flags |= BTRFS_INODE_NODATASUM;
-
- memset(&btrfs_inode, 0, sizeof(btrfs_inode));
- btrfs_set_stack_inode_generation(&btrfs_inode, 1);
- btrfs_set_stack_inode_size(&btrfs_inode, total_bytes);
- btrfs_set_stack_inode_nlink(&btrfs_inode, 1);
- btrfs_set_stack_inode_nbytes(&btrfs_inode, 0);
- btrfs_set_stack_inode_mode(&btrfs_inode, S_IFREG | 0400);
- btrfs_set_stack_inode_flags(&btrfs_inode, flags);
- btrfs_init_path(&path);
trans = btrfs_start_transaction(root, 1);
- BUG_ON(!trans);
+ if (!trans)
+ return -ENOMEM;
- objectid = btrfs_root_dirid(&root->root_item);
- ret = btrfs_find_free_objectid(trans, root, objectid, &objectid);
- if (ret)
- goto fail;
+ cache_tree_init(&used_tmp);
- /*
- * copy blocks covered by extent #0 to new positions. extent #0 is
- * special, we can't rely on relocate_extents_range to relocate it.
- */
- for (last_byte = 0; last_byte < first_free; last_byte += sectorsize) {
- ret = custom_alloc_extent(root, sectorsize, 0, &key, 0);
- if (ret)
- goto fail;
- ret = copy_disk_extent(root, key.objectid, last_byte,
- sectorsize);
- if (ret)
- goto fail;
- ret = btrfs_record_file_extent(trans, root, objectid,
- &btrfs_inode, last_byte,
- key.objectid, sectorsize);
- if (ret)
- goto fail;
- if (datacsum) {
- ret = csum_disk_extent(trans, root, key.objectid,
- sectorsize);
- if (ret)
- goto fail;
- }
- }
+ ret = btrfs_find_free_objectid(trans, root, BTRFS_FIRST_FREE_OBJECTID,
+ &ino);
+ if (ret < 0)
+ goto out;
+ ret = btrfs_new_inode(trans, root, ino, 0600 | S_IFREG);
+ if (ret < 0)
+ goto out;
+ ret = btrfs_add_link(trans, root, ino, BTRFS_FIRST_FREE_OBJECTID, name,
+ strlen(name), BTRFS_FT_REG_FILE, NULL, 1);
+ if (ret < 0)
+ goto out;
- while(1) {
- key.objectid = last_byte;
- key.offset = 0;
- btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
- ret = btrfs_search_slot(trans, fs_info->extent_root,
- &key, &path, 0, 0);
- if (ret < 0)
- goto fail;
-next:
- leaf = path.nodes[0];
- if (path.slots[0] >= btrfs_header_nritems(leaf)) {
- ret = btrfs_next_leaf(extent_root, &path);
- if (ret < 0)
- goto fail;
- if (ret > 0)
- break;
- leaf = path.nodes[0];
- }
- btrfs_item_key_to_cpu(leaf, &key, path.slots[0]);
- if (last_byte > key.objectid ||
- key.type != BTRFS_EXTENT_ITEM_KEY) {
- path.slots[0]++;
- goto next;
- }
+ path = btrfs_alloc_path();
+ if (!path) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ key.objectid = ino;
+ key.type = BTRFS_INODE_ITEM_KEY;
+ key.offset = 0;
- bytenr = key.objectid;
- num_bytes = key.offset;
- ei = btrfs_item_ptr(leaf, path.slots[0],
- struct btrfs_extent_item);
- if (!(btrfs_extent_flags(leaf, ei) & BTRFS_EXTENT_FLAG_DATA)) {
- path.slots[0]++;
- goto next;
- }
+ ret = btrfs_search_slot(trans, root, &key, path, 0, 1);
+ if (ret) {
+ ret = (ret > 0 ? -ENOENT : ret);
+ goto out;
+ }
+ read_extent_buffer(path->nodes[0], &buf,
+ btrfs_item_ptr_offset(path->nodes[0], path->slots[0]),
+ sizeof(buf));
+ btrfs_release_path(path);
- BUG_ON(btrfs_item_size_nr(leaf, path.slots[0]) != sizeof(*ei) +
- btrfs_extent_inline_ref_size(BTRFS_EXTENT_DATA_REF_KEY));
-
- iref = (struct btrfs_extent_inline_ref *)(ei + 1);
- key.type = btrfs_extent_inline_ref_type(leaf, iref);
- BUG_ON(key.type != BTRFS_EXTENT_DATA_REF_KEY);
- dref = (struct btrfs_extent_data_ref *)(&iref->offset);
- if (btrfs_extent_data_ref_root(leaf, dref) !=
- BTRFS_FS_TREE_OBJECTID) {
- path.slots[0]++;
- goto next;
- }
+ /*
+ * Create a new used space cache, which doesn't contain the reserved
+ * range
+ */
+ for (cache = first_cache_extent(&cctx->used); cache;
+ cache = next_cache_extent(cache)) {
+ ret = add_cache_extent(&used_tmp, cache->start, cache->size);
+ if (ret < 0)
+ goto out;
+ }
+ ret = wipe_reserved_ranges(&used_tmp, 0, 0);
+ if (ret < 0)
+ goto out;
- if (bytenr > last_byte) {
- ret = create_image_file_range(trans, root, objectid,
- &btrfs_inode, last_byte,
- bytenr, cctx,
- datacsum);
- if (ret)
- goto fail;
- }
- ret = btrfs_record_file_extent(trans, root, objectid,
- &btrfs_inode, bytenr, bytenr,
- num_bytes);
- if (ret)
- goto fail;
- last_byte = bytenr + num_bytes;
- btrfs_release_path(&path);
+ /*
+ * Start from 1M, as 0~1M is reserved, and create_image_file_range()
+ * can't handle bytenr 0(will consider it as a hole)
+ */
+ cur = 1024 * 1024;
+ while (cur < size) {
+ u64 len = size - cur;
- if (trans->blocks_used >= 4096) {
- ret = btrfs_commit_transaction(trans, root);
- BUG_ON(ret);
- trans = btrfs_start_transaction(root, 1);
- BUG_ON(!trans);
- }
- }
- btrfs_release_path(&path);
- if (total_bytes > last_byte) {
- ret = create_image_file_range(trans, root, objectid,
- &btrfs_inode, last_byte,
- total_bytes, cctx,
- datacsum);
- if (ret)
- goto fail;
+ ret = create_image_file_range(trans, root, &used_tmp,
+ &buf, ino, cur, &len, datacsum);
+ if (ret < 0)
+ goto out;
+ cur += len;
}
+ /* Handle the reserved ranges */
+ ret = migrate_reserved_ranges(trans, root, &cctx->used, &buf, fd, ino,
+ cfg->num_bytes, datacsum);
- ret = btrfs_insert_inode(trans, root, objectid, &btrfs_inode);
- if (ret)
- goto fail;
- location.objectid = objectid;
- location.offset = 0;
- btrfs_set_key_type(&location, BTRFS_INODE_ITEM_KEY);
- ret = btrfs_insert_dir_item(trans, root, name, strlen(name),
- btrfs_root_dirid(&root->root_item),
- &location, BTRFS_FT_REG_FILE, objectid);
- if (ret)
- goto fail;
- ret = btrfs_insert_inode_ref(trans, root, name, strlen(name),
- objectid,
- btrfs_root_dirid(&root->root_item),
- objectid);
- if (ret)
- goto fail;
- location.objectid = btrfs_root_dirid(&root->root_item);
- location.offset = 0;
- btrfs_set_key_type(&location, BTRFS_INODE_ITEM_KEY);
- ret = btrfs_lookup_inode(trans, root, &path, &location, 1);
- if (ret)
- goto fail;
- leaf = path.nodes[0];
- inode_item = btrfs_item_ptr(leaf, path.slots[0],
- struct btrfs_inode_item);
- btrfs_set_inode_size(leaf, inode_item, strlen(name) * 2 +
- btrfs_inode_size(leaf, inode_item));
- btrfs_mark_buffer_dirty(leaf);
- btrfs_release_path(&path);
- ret = btrfs_commit_transaction(trans, root);
- BUG_ON(ret);
-fail:
- btrfs_release_path(&path);
+ key.objectid = ino;
+ key.type = BTRFS_INODE_ITEM_KEY;
+ key.offset = 0;
+ ret = btrfs_search_slot(trans, root, &key, path, 0, 1);
+ if (ret) {
+ ret = (ret > 0 ? -ENOENT : ret);
+ goto out;
+ }
+ btrfs_set_stack_inode_size(&buf, cfg->num_bytes);
+ write_extent_buffer(path->nodes[0], &buf,
+ btrfs_item_ptr_offset(path->nodes[0], path->slots[0]),
+ sizeof(buf));
+out:
+ free_extent_cache_tree(&used_tmp);
+ btrfs_free_path(path);
+ btrfs_commit_transaction(trans, root);
return ret;
}
@@ -1659,129 +1718,6 @@ fail:
return new_root;
}
-static int create_chunk_mapping(struct btrfs_trans_handle *trans,
- struct btrfs_root *root)
-{
- struct btrfs_fs_info *info = root->fs_info;
- struct btrfs_root *chunk_root = info->chunk_root;
- struct btrfs_root *extent_root = info->extent_root;
- struct btrfs_device *device;
- struct btrfs_block_group_cache *cache;
- struct btrfs_dev_extent *extent;
- struct extent_buffer *leaf;
- struct btrfs_chunk chunk;
- struct btrfs_key key;
- struct btrfs_path path;
- u64 cur_start;
- u64 total_bytes;
- u64 chunk_objectid;
- int ret;
-
- btrfs_init_path(&path);
-
- total_bytes = btrfs_super_total_bytes(root->fs_info->super_copy);
- chunk_objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID;
-
- BUG_ON(list_empty(&info->fs_devices->devices));
- device = list_entry(info->fs_devices->devices.next,
- struct btrfs_device, dev_list);
- BUG_ON(device->devid != info->fs_devices->latest_devid);
-
- /* delete device extent created by make_btrfs */
- key.objectid = device->devid;
- key.offset = 0;
- key.type = BTRFS_DEV_EXTENT_KEY;
- ret = btrfs_search_slot(trans, device->dev_root, &key, &path, -1, 1);
- if (ret < 0)
- goto err;
-
- BUG_ON(ret > 0);
- ret = btrfs_del_item(trans, device->dev_root, &path);
- if (ret)
- goto err;
- btrfs_release_path(&path);
-
- /* delete chunk item created by make_btrfs */
- key.objectid = chunk_objectid;
- key.offset = 0;
- key.type = BTRFS_CHUNK_ITEM_KEY;
- ret = btrfs_search_slot(trans, chunk_root, &key, &path, -1, 1);
- if (ret < 0)
- goto err;
-
- BUG_ON(ret > 0);
- ret = btrfs_del_item(trans, chunk_root, &path);
- if (ret)
- goto err;
- btrfs_release_path(&path);
-
- /* for each block group, create device extent and chunk item */
- cur_start = 0;
- while (cur_start < total_bytes) {
- cache = btrfs_lookup_block_group(root->fs_info, cur_start);
- BUG_ON(!cache);
-
- /* insert device extent */
- key.objectid = device->devid;
- key.offset = cache->key.objectid;
- key.type = BTRFS_DEV_EXTENT_KEY;
- ret = btrfs_insert_empty_item(trans, device->dev_root, &path,
- &key, sizeof(*extent));
- if (ret)
- goto err;
-
- leaf = path.nodes[0];
- extent = btrfs_item_ptr(leaf, path.slots[0],
- struct btrfs_dev_extent);
-
- btrfs_set_dev_extent_chunk_tree(leaf, extent,
- chunk_root->root_key.objectid);
- btrfs_set_dev_extent_chunk_objectid(leaf, extent,
- chunk_objectid);
- btrfs_set_dev_extent_chunk_offset(leaf, extent,
- cache->key.objectid);
- btrfs_set_dev_extent_length(leaf, extent, cache->key.offset);
- write_extent_buffer(leaf, root->fs_info->chunk_tree_uuid,
- (unsigned long)btrfs_dev_extent_chunk_tree_uuid(extent),
- BTRFS_UUID_SIZE);
- btrfs_mark_buffer_dirty(leaf);
- btrfs_release_path(&path);
-
- /* insert chunk item */
- btrfs_set_stack_chunk_length(&chunk, cache->key.offset);
- btrfs_set_stack_chunk_owner(&chunk,
- extent_root->root_key.objectid);
- btrfs_set_stack_chunk_stripe_len(&chunk, BTRFS_STRIPE_LEN);
- btrfs_set_stack_chunk_type(&chunk, cache->flags);
- btrfs_set_stack_chunk_io_align(&chunk, device->io_align);
- btrfs_set_stack_chunk_io_width(&chunk, device->io_width);
- btrfs_set_stack_chunk_sector_size(&chunk, device->sector_size);
- btrfs_set_stack_chunk_num_stripes(&chunk, 1);
- btrfs_set_stack_chunk_sub_stripes(&chunk, 0);
- btrfs_set_stack_stripe_devid(&chunk.stripe, device->devid);
- btrfs_set_stack_stripe_offset(&chunk.stripe,
- cache->key.objectid);
- memcpy(&chunk.stripe.dev_uuid, device->uuid, BTRFS_UUID_SIZE);
-
- key.objectid = chunk_objectid;
- key.offset = cache->key.objectid;
- key.type = BTRFS_CHUNK_ITEM_KEY;
-
- ret = btrfs_insert_item(trans, chunk_root, &key, &chunk,
- btrfs_chunk_item_size(1));
- if (ret)
- goto err;
-
- cur_start = cache->key.objectid + cache->key.offset;
- }
-
- device->bytes_used = total_bytes;
- ret = btrfs_update_device(trans, device);
-err:
- btrfs_release_path(&path);
- return ret;
-}
-
static int create_subvol(struct btrfs_trans_handle *trans,
struct btrfs_root *root, u64 root_objectid)
{
@@ -1817,23 +1753,89 @@ static int create_subvol(struct btrfs_trans_handle *trans,
return 0;
}
-static int init_btrfs(struct btrfs_root *root)
+/*
+ * New make_btrfs() has handle system and meta chunks quite well.
+ * So only need to add remaining data chunks.
+ */
+static int make_convert_data_block_groups(struct btrfs_trans_handle *trans,
+ struct btrfs_fs_info *fs_info,
+ struct btrfs_mkfs_config *cfg,
+ struct btrfs_convert_context *cctx)
+{
+ struct btrfs_root *extent_root = fs_info->extent_root;
+ struct cache_tree *data_chunks = &cctx->data_chunks;
+ struct cache_extent *cache;
+ u64 max_chunk_size;
+ int ret = 0;
+
+ /*
+ * Don't create data chunk over 10% of the convert device
+ * And for single chunk, don't create chunk larger than 1G.
+ */
+ max_chunk_size = cfg->num_bytes / 10;
+ max_chunk_size = min((u64)(1024 * 1024 * 1024), max_chunk_size);
+ max_chunk_size = round_down(max_chunk_size, extent_root->sectorsize);
+
+ for (cache = first_cache_extent(data_chunks); cache;
+ cache = next_cache_extent(cache)) {
+ u64 cur = cache->start;
+
+ while (cur < cache->start + cache->size) {
+ u64 len;
+ u64 cur_backup = cur;
+
+ len = min(max_chunk_size,
+ cache->start + cache->size - cur);
+ ret = btrfs_alloc_data_chunk(trans, extent_root,
+ &cur_backup, len,
+ BTRFS_BLOCK_GROUP_DATA, 1);
+ if (ret < 0)
+ break;
+ ret = btrfs_make_block_group(trans, extent_root, 0,
+ BTRFS_BLOCK_GROUP_DATA,
+ BTRFS_FIRST_CHUNK_TREE_OBJECTID,
+ cur, len);
+ if (ret < 0)
+ break;
+ cur += len;
+ }
+ }
+ return ret;
+}
+
+/*
+ * Init the temp btrfs to a operational status.
+ *
+ * It will fix the extent usage accounting(XXX: Do we really need?) and
+ * insert needed data chunks, to ensure all old fs data extents are covered
+ * by DATA chunks, preventing wrong chunks are allocated.
+ *
+ * And also create convert image subvolume and relocation tree.
+ * (XXX: Not need again?)
+ * But the convert image subvolume is *NOT* linked to fs tree yet.
+ */
+static int init_btrfs(struct btrfs_mkfs_config *cfg, struct btrfs_root *root,
+ struct btrfs_convert_context *cctx, int datacsum,
+ int packing, int noxattr)
{
- int ret;
struct btrfs_key location;
struct btrfs_trans_handle *trans;
struct btrfs_fs_info *fs_info = root->fs_info;
- struct extent_buffer *tmp;
+ int ret;
+ /*
+ * Don't alloc any metadata/system chunk, as we don't want
+ * any meta/sys chunk allcated before all data chunks are inserted.
+ * Or we screw up the chunk layout just like the old implement.
+ */
+ fs_info->avoid_sys_chunk_alloc = 1;
+ fs_info->avoid_meta_chunk_alloc = 1;
trans = btrfs_start_transaction(root, 1);
BUG_ON(!trans);
- ret = btrfs_make_block_groups(trans, root);
- if (ret)
- goto err;
ret = btrfs_fix_block_accounting(trans, root);
if (ret)
goto err;
- ret = create_chunk_mapping(trans, root);
+ ret = make_convert_data_block_groups(trans, fs_info, cfg, cctx);
if (ret)
goto err;
ret = btrfs_make_root_dir(trans, fs_info->tree_root,
@@ -1857,19 +1859,16 @@ static int init_btrfs(struct btrfs_root *root)
/* subvol for fs image file */
ret = create_subvol(trans, root, CONV_IMAGE_SUBVOL_OBJECTID);
- BUG_ON(ret);
- /* subvol for data relocation */
+ if (ret < 0)
+ goto err;
+ /* subvol for data relocation tree */
ret = create_subvol(trans, root, BTRFS_DATA_RELOC_TREE_OBJECTID);
- BUG_ON(ret);
-
- extent_buffer_get(fs_info->csum_root->node);
- ret = __btrfs_cow_block(trans, fs_info->csum_root,
- fs_info->csum_root->node, NULL, 0, &tmp, 0, 0);
- BUG_ON(ret);
- free_extent_buffer(tmp);
+ if (ret < 0)
+ goto err;
ret = btrfs_commit_transaction(trans, root);
- BUG_ON(ret);
+ fs_info->avoid_sys_chunk_alloc = 0;
+ fs_info->avoid_meta_chunk_alloc = 0;
err:
return ret;
}
@@ -1959,512 +1958,331 @@ static int prepare_system_chunk_sb(struct btrfs_super_block *super)
return 0;
}
-static int prepare_system_chunk(int fd, u64 sb_bytenr)
-{
- int ret;
- struct extent_buffer *buf;
- struct btrfs_super_block *super;
+static const struct btrfs_convert_operations ext2_convert_ops = {
+ .name = "ext2",
+ .open_fs = ext2_open_fs,
+ .read_used_space = ext2_read_used_space,
+ .copy_inodes = ext2_copy_inodes,
+ .close_fs = ext2_close_fs,
+};
- BUG_ON(BTRFS_SUPER_INFO_SIZE < sizeof(*super));
- buf = malloc(sizeof(*buf) + BTRFS_SUPER_INFO_SIZE);
- if (!buf)
- return -ENOMEM;
+static const struct btrfs_convert_operations *convert_operations[] = {
+ &ext2_convert_ops,
+};
- buf->len = BTRFS_SUPER_INFO_SIZE;
- ret = pread(fd, buf->data, BTRFS_SUPER_INFO_SIZE, sb_bytenr);
- if (ret != BTRFS_SUPER_INFO_SIZE)
- goto fail;
+static int convert_open_fs(const char *devname,
+ struct btrfs_convert_context *cctx)
+{
+ int i;
- super = (struct btrfs_super_block *)buf->data;
- BUG_ON(btrfs_super_bytenr(super) != sb_bytenr);
- BUG_ON(btrfs_super_num_devices(super) != 1);
+ memset(cctx, 0, sizeof(*cctx));
- ret = prepare_system_chunk_sb(super);
- if (ret)
- goto fail;
+ for (i = 0; i < ARRAY_SIZE(convert_operations); i++) {
+ int ret = convert_operations[i]->open_fs(cctx, devname);
- csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
- ret = pwrite(fd, buf->data, BTRFS_SUPER_INFO_SIZE, sb_bytenr);
- if (ret != BTRFS_SUPER_INFO_SIZE)
- goto fail;
+ if (ret == 0) {
+ cctx->convert_ops = convert_operations[i];
+ return ret;
+ }
+ }
- ret = 0;
-fail:
- free(buf);
- if (ret > 0)
- ret = -1;
- return ret;
+ fprintf(stderr, "No file system found to convert.\n");
+ return -1;
}
-static int relocate_one_reference(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
- u64 extent_start, u64 extent_size,
- struct btrfs_key *extent_key,
- struct extent_io_tree *reloc_tree)
+/*
+ * Helper for expand and merge extent_cache for wipe_one_reserved_range() to
+ * handle wiping a range that exists in cache.
+ */
+static int _expand_extent_cache(struct cache_tree *tree,
+ struct cache_extent *entry,
+ u64 min_stripe_size, int backward)
{
- struct extent_buffer *leaf;
- struct btrfs_file_extent_item *fi;
- struct btrfs_key key;
- struct btrfs_path path;
- struct btrfs_inode_item inode;
- struct blk_iterate_data data;
- u64 bytenr;
- u64 num_bytes;
- u64 cur_offset;
- u64 new_pos;
- u64 nbytes;
- u64 sector_end;
- u32 sectorsize = root->sectorsize;
- unsigned long ptr;
- int datacsum;
- int fd;
- int ret;
-
- btrfs_init_path(&path);
- ret = btrfs_search_slot(trans, root, extent_key, &path, -1, 1);
- if (ret)
- goto fail;
-
- leaf = path.nodes[0];
- fi = btrfs_item_ptr(leaf, path.slots[0],
- struct btrfs_file_extent_item);
- BUG_ON(btrfs_file_extent_offset(leaf, fi) > 0);
- if (extent_start != btrfs_file_extent_disk_bytenr(leaf, fi) ||
- extent_size != btrfs_file_extent_disk_num_bytes(leaf, fi)) {
- ret = 1;
- goto fail;
- }
-
- bytenr = extent_start + btrfs_file_extent_offset(leaf, fi);
- num_bytes = btrfs_file_extent_num_bytes(leaf, fi);
-
- ret = btrfs_del_item(trans, root, &path);
- if (ret)
- goto fail;
-
- ret = btrfs_free_extent(trans, root, extent_start, extent_size, 0,
- root->root_key.objectid,
- extent_key->objectid, extent_key->offset);
- if (ret)
- goto fail;
-
- btrfs_release_path(&path);
-
- key.objectid = extent_key->objectid;
- key.offset = 0;
- key.type = BTRFS_INODE_ITEM_KEY;
- ret = btrfs_lookup_inode(trans, root, &path, &key, 0);
- if (ret)
- goto fail;
-
- leaf = path.nodes[0];
- ptr = btrfs_item_ptr_offset(leaf, path.slots[0]);
- read_extent_buffer(leaf, &inode, ptr, sizeof(inode));
- btrfs_release_path(&path);
+ struct cache_extent *ce;
+ int diff;
- BUG_ON(num_bytes & (sectorsize - 1));
- nbytes = btrfs_stack_inode_nbytes(&inode) - num_bytes;
- btrfs_set_stack_inode_nbytes(&inode, nbytes);
- datacsum = !(btrfs_stack_inode_flags(&inode) & BTRFS_INODE_NODATASUM);
-
- init_blk_iterate_data(&data, trans, root, &inode, extent_key->objectid,
- datacsum);
- data.first_block = extent_key->offset;
-
- cur_offset = extent_key->offset;
- while (num_bytes > 0) {
- sector_end = bytenr + sectorsize - 1;
- if (test_range_bit(reloc_tree, bytenr, sector_end,
- EXTENT_LOCKED, 1)) {
- ret = get_state_private(reloc_tree, bytenr, &new_pos);
- BUG_ON(ret);
- } else {
- ret = custom_alloc_extent(root, sectorsize, 0, &key, 0);
- if (ret)
- goto fail;
- new_pos = key.objectid;
-
- if (cur_offset == extent_key->offset) {
- fd = root->fs_info->fs_devices->latest_bdev;
- readahead(fd, bytenr, num_bytes);
- }
- ret = copy_disk_extent(root, new_pos, bytenr,
- sectorsize);
- if (ret)
- goto fail;
- ret = set_extent_bits(reloc_tree, bytenr, sector_end,
- EXTENT_LOCKED, GFP_NOFS);
- BUG_ON(ret);
- ret = set_state_private(reloc_tree, bytenr, new_pos);
- BUG_ON(ret);
+ if (entry->size >= min_stripe_size)
+ return 0;
+ diff = min_stripe_size - entry->size;
+
+ if (backward) {
+ ce = prev_cache_extent(entry);
+ if (!ce)
+ goto expand_back;
+ if (ce->start + ce->size >= entry->start - diff) {
+ /* Directly merge with previous extent */
+ ce->size = entry->start + entry->size - ce->start;
+ remove_cache_extent(tree, entry);
+ free(entry);
+ return 0;
}
-
- ret = block_iterate_proc(new_pos / sectorsize,
- cur_offset / sectorsize, &data);
- if (ret < 0)
- goto fail;
-
- cur_offset += sectorsize;
- bytenr += sectorsize;
- num_bytes -= sectorsize;
+expand_back:
+ /* No overlap, normal extent */
+ if (entry->start < diff) {
+ error("cannot find space for data chunk layout");
+ return -ENOSPC;
+ }
+ entry->start -= diff;
+ entry->size += diff;
+ return 0;
}
-
- if (data.num_blocks > 0) {
- ret = record_file_blocks(&data, data.first_block,
- data.disk_block, data.num_blocks);
- if (ret)
- goto fail;
+ ce = next_cache_extent(entry);
+ if (!ce)
+ goto expand_after;
+ if (entry->start + entry->size + diff >= ce->start) {
+ /* Directly merge with next extent */
+ entry->size = ce->start + ce->size - entry->start;
+ remove_cache_extent(tree, ce);
+ free(ce);
+ return 0;
}
-
- key.objectid = extent_key->objectid;
- key.offset = 0;
- key.type = BTRFS_INODE_ITEM_KEY;
- ret = btrfs_lookup_inode(trans, root, &path, &key, 1);
- if (ret)
- goto fail;
-
- leaf = path.nodes[0];
- ptr = btrfs_item_ptr_offset(leaf, path.slots[0]);
- write_extent_buffer(leaf, &inode, ptr, sizeof(inode));
- btrfs_mark_buffer_dirty(leaf);
- btrfs_release_path(&path);
-
-fail:
- btrfs_release_path(&path);
- return ret;
+expand_after:
+ entry->size += diff;
+ return 0;
}
-static int relocate_extents_range(struct btrfs_root *fs_root,
- struct btrfs_root *image_root,
- u64 start_byte, u64 end_byte)
+/*
+ * Remove one reserve range from given cache tree
+ * if min_stripe_size is non-zero, it will ensure for split case,
+ * all its split cache extent is no smaller than @min_strip_size / 2.
+ */
+static int wipe_one_reserved_range(struct cache_tree *tree,
+ u64 start, u64 len, u64 min_stripe_size,
+ int ensure_size)
{
- struct btrfs_fs_info *info = fs_root->fs_info;
- struct btrfs_root *extent_root = info->extent_root;
- struct btrfs_root *cur_root = NULL;
- struct btrfs_trans_handle *trans;
- struct btrfs_extent_data_ref *dref;
- struct btrfs_extent_inline_ref *iref;
- struct btrfs_extent_item *ei;
- struct extent_buffer *leaf;
- struct btrfs_key key;
- struct btrfs_key extent_key;
- struct btrfs_path path;
- struct extent_io_tree reloc_tree;
- unsigned long ptr;
- unsigned long end;
- u64 cur_byte;
- u64 num_bytes;
- u64 ref_root;
- u64 num_extents;
- int pass = 0;
+ struct cache_extent *cache;
int ret;
- btrfs_init_path(&path);
- extent_io_tree_init(&reloc_tree);
+ BUG_ON(ensure_size && min_stripe_size == 0);
+ /*
+ * The logical here is simplified to handle special cases only
+ * So we don't need to consider merge case for ensure_size
+ */
+ BUG_ON(min_stripe_size && (min_stripe_size < len * 2 ||
+ min_stripe_size / 2 < BTRFS_STRIPE_LEN));
- key.objectid = start_byte;
- key.offset = 0;
- key.type = BTRFS_EXTENT_ITEM_KEY;
- ret = btrfs_search_slot(NULL, extent_root, &key, &path, 0, 0);
- if (ret < 0)
- goto fail;
- if (ret > 0) {
- ret = btrfs_previous_item(extent_root, &path, 0,
- BTRFS_EXTENT_ITEM_KEY);
- if (ret < 0)
- goto fail;
- if (ret == 0) {
- leaf = path.nodes[0];
- btrfs_item_key_to_cpu(leaf, &key, path.slots[0]);
- if (key.objectid + key.offset > start_byte)
- start_byte = key.objectid;
- }
- }
- btrfs_release_path(&path);
-again:
- cur_root = (pass % 2 == 0) ? image_root : fs_root;
- num_extents = 0;
+ /* Also, wipe range should already be aligned */
+ BUG_ON(start != round_down(start, BTRFS_STRIPE_LEN) ||
+ start + len != round_up(start + len, BTRFS_STRIPE_LEN));
- trans = btrfs_start_transaction(cur_root, 1);
- BUG_ON(!trans);
+ min_stripe_size /= 2;
- cur_byte = start_byte;
- while (1) {
- key.objectid = cur_byte;
- key.offset = 0;
- key.type = BTRFS_EXTENT_ITEM_KEY;
- ret = btrfs_search_slot(trans, extent_root,
- &key, &path, 0, 0);
- if (ret < 0)
- goto fail;
-next:
- leaf = path.nodes[0];
- if (path.slots[0] >= btrfs_header_nritems(leaf)) {
- ret = btrfs_next_leaf(extent_root, &path);
- if (ret < 0)
- goto fail;
- if (ret > 0)
- break;
- leaf = path.nodes[0];
- }
+ cache = lookup_cache_extent(tree, start, len);
+ if (!cache)
+ return 0;
- btrfs_item_key_to_cpu(leaf, &key, path.slots[0]);
- if (key.objectid < cur_byte ||
- key.type != BTRFS_EXTENT_ITEM_KEY) {
- path.slots[0]++;
- goto next;
+ if (start <= cache->start) {
+ /*
+ * |--------cache---------|
+ * |-wipe-|
+ */
+ BUG_ON(start + len <= cache->start);
+
+ /*
+ * The wipe size is smaller than min_stripe_size / 2,
+ * so the result length should still meet min_stripe_size
+ * And no need to do alignment
+ */
+ cache->size -= (start + len - cache->start);
+ if (cache->size == 0) {
+ remove_cache_extent(tree, cache);
+ free(cache);
+ return 0;
}
- if (key.objectid >= end_byte)
- break;
-
- num_extents++;
-
- cur_byte = key.objectid;
- num_bytes = key.offset;
- ei = btrfs_item_ptr(leaf, path.slots[0],
- struct btrfs_extent_item);
- BUG_ON(!(btrfs_extent_flags(leaf, ei) &
- BTRFS_EXTENT_FLAG_DATA));
-
- ptr = btrfs_item_ptr_offset(leaf, path.slots[0]);
- end = ptr + btrfs_item_size_nr(leaf, path.slots[0]);
-
- ptr += sizeof(struct btrfs_extent_item);
-
- while (ptr < end) {
- iref = (struct btrfs_extent_inline_ref *)ptr;
- key.type = btrfs_extent_inline_ref_type(leaf, iref);
- BUG_ON(key.type != BTRFS_EXTENT_DATA_REF_KEY);
- dref = (struct btrfs_extent_data_ref *)(&iref->offset);
- ref_root = btrfs_extent_data_ref_root(leaf, dref);
- extent_key.objectid =
- btrfs_extent_data_ref_objectid(leaf, dref);
- extent_key.offset =
- btrfs_extent_data_ref_offset(leaf, dref);
- extent_key.type = BTRFS_EXTENT_DATA_KEY;
- BUG_ON(btrfs_extent_data_ref_count(leaf, dref) != 1);
-
- if (ref_root == cur_root->root_key.objectid)
- break;
- ptr += btrfs_extent_inline_ref_size(key.type);
- }
+ BUG_ON(ensure_size && cache->size < min_stripe_size);
- if (ptr >= end) {
- path.slots[0]++;
- goto next;
+ cache->start = start + len;
+ return 0;
+ } else if (start > cache->start && start + len < cache->start +
+ cache->size) {
+ /*
+ * |-------cache-----|
+ * |-wipe-|
+ */
+ u64 old_start = cache->start;
+ u64 old_len = cache->size;
+ u64 insert_start = start + len;
+ u64 insert_len;
+
+ cache->size = start - cache->start;
+ /* Expand the leading half part if needed */
+ if (ensure_size && cache->size < min_stripe_size) {
+ ret = _expand_extent_cache(tree, cache,
+ min_stripe_size, 1);
+ if (ret < 0)
+ return ret;
}
- ret = relocate_one_reference(trans, cur_root, cur_byte,
- num_bytes, &extent_key,
- &reloc_tree);
+ /* And insert the new one */
+ insert_len = old_start + old_len - start - len;
+ ret = add_merge_cache_extent(tree, insert_start, insert_len);
if (ret < 0)
- goto fail;
-
- cur_byte += num_bytes;
- btrfs_release_path(&path);
+ return ret;
- if (trans->blocks_used >= 4096) {
- ret = btrfs_commit_transaction(trans, cur_root);
- BUG_ON(ret);
- trans = btrfs_start_transaction(cur_root, 1);
- BUG_ON(!trans);
+ /* Expand the last half part if needed */
+ if (ensure_size && insert_len < min_stripe_size) {
+ cache = lookup_cache_extent(tree, insert_start,
+ insert_len);
+ if (!cache || cache->start != insert_start ||
+ cache->size != insert_len)
+ return -ENOENT;
+ ret = _expand_extent_cache(tree, cache,
+ min_stripe_size, 0);
}
- }
- btrfs_release_path(&path);
-
- ret = btrfs_commit_transaction(trans, cur_root);
- BUG_ON(ret);
-
- if (num_extents > 0 && pass++ < 16)
- goto again;
- ret = (num_extents > 0) ? -1 : 0;
-fail:
- btrfs_release_path(&path);
- extent_io_tree_cleanup(&reloc_tree);
- return ret;
+ return ret;
+ }
+ /*
+ * |----cache-----|
+ * |--wipe-|
+ * Wipe len should be small enough and no need to expand the
+ * remaining extent
+ */
+ cache->size = start - cache->start;
+ BUG_ON(ensure_size && cache->size < min_stripe_size);
+ return 0;
}
/*
- * relocate data in system chunk
+ * Remove reserved ranges from given cache_tree
+ *
+ * It will remove the following ranges
+ * 1) 0~1M
+ * 2) 2nd superblock, +64K (make sure chunks are 64K aligned)
+ * 3) 3rd superblock, +64K
+ *
+ * @min_stripe must be given for safety check
+ * and if @ensure_size is given, it will ensure affected cache_extent will be
+ * larger than min_stripe_size
*/
-static int cleanup_sys_chunk(struct btrfs_root *fs_root,
- struct btrfs_root *image_root)
+static int wipe_reserved_ranges(struct cache_tree *tree, u64 min_stripe_size,
+ int ensure_size)
{
- struct btrfs_block_group_cache *cache;
- int i, ret = 0;
- u64 offset = 0;
- u64 end_byte;
-
- while(1) {
- cache = btrfs_lookup_block_group(fs_root->fs_info, offset);
- if (!cache)
- break;
-
- end_byte = cache->key.objectid + cache->key.offset;
- if (cache->flags & BTRFS_BLOCK_GROUP_SYSTEM) {
- ret = relocate_extents_range(fs_root, image_root,
- cache->key.objectid,
- end_byte);
- if (ret)
- goto fail;
- }
- offset = end_byte;
- }
- for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) {
- offset = btrfs_sb_offset(i);
- offset &= ~((u64)BTRFS_STRIPE_LEN - 1);
+ int ret;
- ret = relocate_extents_range(fs_root, image_root,
- offset, offset + BTRFS_STRIPE_LEN);
- if (ret)
- goto fail;
- }
- ret = 0;
-fail:
+ ret = wipe_one_reserved_range(tree, 0, 1024 * 1024, min_stripe_size,
+ ensure_size);
+ if (ret < 0)
+ return ret;
+ ret = wipe_one_reserved_range(tree, btrfs_sb_offset(1),
+ BTRFS_STRIPE_LEN, min_stripe_size, ensure_size);
+ if (ret < 0)
+ return ret;
+ ret = wipe_one_reserved_range(tree, btrfs_sb_offset(2),
+ BTRFS_STRIPE_LEN, min_stripe_size, ensure_size);
return ret;
}
-static int fixup_chunk_mapping(struct btrfs_root *root)
+static int calculate_available_space(struct btrfs_convert_context *cctx)
{
- struct btrfs_trans_handle *trans;
- struct btrfs_fs_info *info = root->fs_info;
- struct btrfs_root *chunk_root = info->chunk_root;
- struct extent_buffer *leaf;
- struct btrfs_key key;
- struct btrfs_path path;
- struct btrfs_chunk chunk;
- unsigned long ptr;
- u32 size;
- u64 type;
- int ret;
-
- btrfs_init_path(&path);
-
- trans = btrfs_start_transaction(root, 1);
- BUG_ON(!trans);
-
+ struct cache_tree *used = &cctx->used;
+ struct cache_tree *data_chunks = &cctx->data_chunks;
+ struct cache_tree *free = &cctx->free;
+ struct cache_extent *cache;
+ u64 cur_off = 0;
/*
- * recow the whole chunk tree. this will move all chunk tree blocks
- * into system block group.
+ * Twice the minimal chunk size, to allow later wipe_reserved_ranges()
+ * works without need to consider overlap
*/
- memset(&key, 0, sizeof(key));
- while (1) {
- ret = btrfs_search_slot(trans, chunk_root, &key, &path, 0, 1);
- if (ret < 0)
- goto err;
+ u64 min_stripe_size = 2 * 16 * 1024 * 1024;
+ int ret;
- ret = btrfs_next_leaf(chunk_root, &path);
- if (ret < 0)
- goto err;
- if (ret > 0)
- break;
+ /* Calculate data_chunks */
+ for (cache = first_cache_extent(used); cache;
+ cache = next_cache_extent(cache)) {
+ u64 cur_len;
- btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
- btrfs_release_path(&path);
+ if (cache->start + cache->size < cur_off)
+ continue;
+ if (cache->start > cur_off + min_stripe_size)
+ cur_off = cache->start;
+ cur_len = max(cache->start + cache->size - cur_off,
+ min_stripe_size);
+ ret = add_merge_cache_extent(data_chunks, cur_off, cur_len);
+ if (ret < 0)
+ goto out;
+ cur_off += cur_len;
}
- btrfs_release_path(&path);
+ /*
+ * remove reserved ranges, so we won't ever bother relocating an old
+ * filesystem extent to other place.
+ */
+ ret = wipe_reserved_ranges(data_chunks, min_stripe_size, 1);
+ if (ret < 0)
+ goto out;
- /* fixup the system chunk array in super block */
- btrfs_set_super_sys_array_size(info->super_copy, 0);
+ cur_off = 0;
+ /*
+ * Calculate free space
+ * Always round up the start bytenr, to avoid metadata extent corss
+ * stripe boundary, as later mkfs_convert() won't have all the extent
+ * allocation check
+ */
+ for (cache = first_cache_extent(data_chunks); cache;
+ cache = next_cache_extent(cache)) {
+ if (cache->start < cur_off)
+ continue;
+ if (cache->start > cur_off) {
+ u64 insert_start;
+ u64 len;
- key.objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID;
- key.offset = 0;
- key.type = BTRFS_CHUNK_ITEM_KEY;
+ len = cache->start - round_up(cur_off,
+ BTRFS_STRIPE_LEN);
+ insert_start = round_up(cur_off, BTRFS_STRIPE_LEN);
- ret = btrfs_search_slot(trans, chunk_root, &key, &path, 0, 0);
- if (ret < 0)
- goto err;
- BUG_ON(ret != 0);
- while(1) {
- leaf = path.nodes[0];
- if (path.slots[0] >= btrfs_header_nritems(leaf)) {
- ret = btrfs_next_leaf(chunk_root, &path);
+ ret = add_merge_cache_extent(free, insert_start, len);
if (ret < 0)
- goto err;
- if (ret > 0)
- break;
- leaf = path.nodes[0];
+ goto out;
}
- btrfs_item_key_to_cpu(leaf, &key, path.slots[0]);
- if (key.type != BTRFS_CHUNK_ITEM_KEY)
- goto next;
-
- ptr = btrfs_item_ptr_offset(leaf, path.slots[0]);
- size = btrfs_item_size_nr(leaf, path.slots[0]);
- BUG_ON(size != sizeof(chunk));
- read_extent_buffer(leaf, &chunk, ptr, size);
- type = btrfs_stack_chunk_type(&chunk);
+ cur_off = cache->start + cache->size;
+ }
+ /* Don't forget the last range */
+ if (cctx->total_bytes > cur_off) {
+ u64 len = cctx->total_bytes - cur_off;
+ u64 insert_start;
- if (!(type & BTRFS_BLOCK_GROUP_SYSTEM))
- goto next;
+ insert_start = round_up(cur_off, BTRFS_STRIPE_LEN);
- ret = btrfs_add_system_chunk(trans, chunk_root, &key,
- &chunk, size);
- if (ret)
- goto err;
-next:
- path.slots[0]++;
+ ret = add_merge_cache_extent(free, insert_start, len);
+ if (ret < 0)
+ goto out;
}
- ret = btrfs_commit_transaction(trans, root);
- BUG_ON(ret);
-err:
- btrfs_release_path(&path);
+ /* Remove reserved bytes */
+ ret = wipe_reserved_ranges(free, min_stripe_size, 0);
+out:
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)
+/*
+ * Read used space, and since we have the used space,
+ * calcuate data_chunks and free for later mkfs
+ */
+static int convert_read_used_space(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);
+ int ret;
- if (ret == 0) {
- cctx->convert_ops = convert_operations[i];
- return ret;
- }
- }
+ ret = cctx->convert_ops->read_used_space(cctx);
+ if (ret)
+ return ret;
- fprintf(stderr, "No file system found to convert.\n");
- return -1;
+ ret = calculate_available_space(cctx);
+ return ret;
}
-static int do_convert(const char *devname, int datacsum, int packing, int noxattr,
- u32 nodesize, int copylabel, const char *fslabel, int progress,
- u64 features)
+static int do_convert(const char *devname, int datacsum, int packing,
+ int noxattr, u32 nodesize, int copylabel, const char *fslabel,
+ int progress, u64 features)
{
- int i, ret, blocks_per_node;
+ int ret;
int fd = -1;
int is_btrfs = 0;
u32 blocksize;
- u64 blocks[7];
u64 total_bytes;
- u64 super_bytenr;
struct btrfs_root *root;
struct btrfs_root *image_root;
struct btrfs_convert_context cctx;
+ struct btrfs_key key;
char *subvol_name = NULL;
struct task_ctx ctx;
char features_buf[64];
@@ -2474,6 +2292,9 @@ static int do_convert(const char *devname, int datacsum, int packing, int noxatt
ret = convert_open_fs(devname, &cctx);
if (ret)
goto fail;
+ ret = convert_read_used_space(&cctx);
+ if (ret)
+ goto fail;
blocksize = cctx.blocksize;
total_bytes = (u64)blocksize * (u64)cctx.block_count;
@@ -2483,22 +2304,6 @@ static int do_convert(const char *devname, int datacsum, int packing, int noxatt
}
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 = convert_alloc_block(&cctx, 0, blocks + i);
- else
- ret = convert_alloc_block_range(&cctx,
- ret + blocks_per_node, blocks_per_node,
- blocks + i);
- if (ret) {
- fprintf(stderr, "not enough free space\n");
- goto fail;
- }
- blocks[i] *= blocksize;
- }
- super_bytenr = blocks[0];
fd = open(devname, O_RDWR);
if (fd < 0) {
fprintf(stderr, "unable to open %s\n", devname);
@@ -2514,57 +2319,65 @@ static int do_convert(const char *devname, int datacsum, int packing, int noxatt
printf("\tfeatures: %s\n", features_buf);
mkfs_cfg.label = cctx.volume_name;
- mkfs_cfg.fs_uuid = NULL;
- memcpy(mkfs_cfg.blocks, blocks, sizeof(blocks));
mkfs_cfg.num_bytes = total_bytes;
mkfs_cfg.nodesize = nodesize;
mkfs_cfg.sectorsize = blocksize;
mkfs_cfg.stripesize = blocksize;
mkfs_cfg.features = features;
+ /* New convert need these space */
+ mkfs_cfg.fs_uuid = malloc(BTRFS_UUID_UNPARSED_SIZE);
+ mkfs_cfg.chunk_uuid = malloc(BTRFS_UUID_UNPARSED_SIZE);
+ *(mkfs_cfg.fs_uuid) = '\0';
+ *(mkfs_cfg.chunk_uuid) = '\0';
- ret = make_btrfs(fd, &mkfs_cfg);
+ ret = make_btrfs(fd, &mkfs_cfg, &cctx);
if (ret) {
fprintf(stderr, "unable to create initial ctree: %s\n",
strerror(-ret));
goto fail;
}
- /* create a system chunk that maps the whole device */
- ret = prepare_system_chunk(fd, super_bytenr);
- if (ret) {
- fprintf(stderr, "unable to update system chunk\n");
- goto fail;
- }
- root = open_ctree_fd(fd, devname, super_bytenr, OPEN_CTREE_WRITES);
+
+ root = open_ctree_fd(fd, devname, mkfs_cfg.super_bytenr,
+ OPEN_CTREE_WRITES);
if (!root) {
fprintf(stderr, "unable to open ctree\n");
goto fail;
}
- ret = cache_free_extents(root, &cctx);
+ ret = init_btrfs(&mkfs_cfg, root, &cctx, datacsum, packing, noxattr);
if (ret) {
- fprintf(stderr, "error during cache_free_extents %d\n", ret);
+ fprintf(stderr, "unable to setup the root tree\n");
goto fail;
}
- root->fs_info->extent_ops = &extent_ops;
- /* recover block allocation bitmap */
- for (i = 0; i < 7; i++) {
- blocks[i] /= blocksize;
- if (nodesize == blocksize)
- convert_free_block(&cctx, blocks[i]);
- else
- convert_free_block_range(&cctx, blocks[i],
- blocks_per_node);
+
+ 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;
+ }
+ key.objectid = CONV_IMAGE_SUBVOL_OBJECTID;
+ key.offset = (u64)-1;
+ key.type = BTRFS_ROOT_ITEM_KEY;
+ image_root = btrfs_read_fs_root(root->fs_info, &key);
+ if (!image_root) {
+ fprintf(stderr, "unable to create subvol\n");
+ goto fail;
}
- ret = init_btrfs(root);
+ ret = create_image(image_root, &mkfs_cfg, &cctx, fd,
+ mkfs_cfg.num_bytes, "image", datacsum);
if (ret) {
- fprintf(stderr, "unable to setup the root tree\n");
+ fprintf(stderr, "error during create_image %d\n", ret);
goto fail;
}
+
printf("creating btrfs metadata.\n");
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);
+ ctx.info = task_init(print_copied_inodes, after_copied_inodes,
+ &ctx);
task_start(ctx.info);
}
ret = copy_inodes(&cctx, root, datacsum, packing, noxattr, &ctx);
@@ -2577,27 +2390,10 @@ static int do_convert(const char *devname, int datacsum, int packing, int noxatt
task_deinit(ctx.info);
}
- 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_image(&cctx, image_root, "image", datacsum);
- if (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_null(root->fs_info->super_copy->label,
@@ -2609,12 +2405,6 @@ static int do_convert(const char *devname, int datacsum, int packing, int noxatt
fprintf(stderr, "set label to '%s'\n", fslabel);
}
- printf("cleaning up system chunk.\n");
- ret = cleanup_sys_chunk(root, image_root);
- if (ret) {
- fprintf(stderr, "error during cleanup_sys_chunk %d\n", ret);
- goto fail;
- }
ret = close_ctree(root);
if (ret) {
fprintf(stderr, "error during close_ctree %d\n", ret);
@@ -2627,7 +2417,7 @@ static int do_convert(const char *devname, int datacsum, int packing, int noxatt
* If this step succeed, we get a mountable btrfs. Otherwise
* the source fs is left unchanged.
*/
- ret = migrate_super_block(fd, super_bytenr, blocksize);
+ ret = migrate_super_block(fd, mkfs_cfg.super_bytenr, blocksize);
if (ret) {
fprintf(stderr, "unable to migrate super block\n");
goto fail;
@@ -2639,13 +2429,6 @@ static int do_convert(const char *devname, int datacsum, int packing, int noxatt
fprintf(stderr, "unable to open ctree\n");
goto fail;
}
- /* move chunk tree into system chunk. */
- ret = fixup_chunk_mapping(root);
- if (ret) {
- fprintf(stderr, "error during fixup_chunk_tree\n");
- goto fail;
- }
- ret = close_ctree(root);
close(fd);
printf("conversion complete.\n");
@@ -2656,12 +2439,72 @@ fail:
close(fd);
if (is_btrfs)
fprintf(stderr,
- "WARNING: an error occured during chunk mapping fixup, filesystem mountable but not finalized\n");
+ "WARNING: an error occurred during chunk mapping fixup, filesystem mountable but not finalized\n");
else
fprintf(stderr, "conversion aborted\n");
return -1;
}
+/*
+ * Check if a non 1:1 mapped chunk can be rolled back.
+ * For new convert, it's OK while for old convert it's not.
+ */
+static int may_rollback_chunk(struct btrfs_fs_info *fs_info, u64 bytenr)
+{
+ struct btrfs_block_group_cache *bg;
+ struct btrfs_key key;
+ struct btrfs_path path;
+ struct btrfs_root *extent_root = fs_info->extent_root;
+ u64 bg_start;
+ u64 bg_end;
+ int ret;
+
+ bg = btrfs_lookup_first_block_group(fs_info, bytenr);
+ if (!bg)
+ return -ENOENT;
+ bg_start = bg->key.objectid;
+ bg_end = bg->key.objectid + bg->key.offset;
+
+ key.objectid = bg_end;
+ key.type = BTRFS_METADATA_ITEM_KEY;
+ key.offset = 0;
+ btrfs_init_path(&path);
+
+ ret = btrfs_search_slot(NULL, extent_root, &key, &path, 0, 0);
+ if (ret < 0)
+ return ret;
+
+ while (1) {
+ struct btrfs_extent_item *ei;
+
+ ret = btrfs_previous_extent_item(extent_root, &path, bg_start);
+ if (ret > 0) {
+ ret = 0;
+ break;
+ }
+ if (ret < 0)
+ break;
+
+ btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
+ if (key.type == BTRFS_METADATA_ITEM_KEY)
+ continue;
+ /* Now it's EXTENT_ITEM_KEY only */
+ ei = btrfs_item_ptr(path.nodes[0], path.slots[0],
+ struct btrfs_extent_item);
+ /*
+ * Found data extent, means this is old convert must follow 1:1
+ * mapping.
+ */
+ if (btrfs_extent_flags(path.nodes[0], ei)
+ & BTRFS_EXTENT_FLAG_DATA) {
+ ret = -EINVAL;
+ break;
+ }
+ }
+ btrfs_release_path(&path);
+ return ret;
+}
+
static int may_rollback(struct btrfs_root *root)
{
struct btrfs_fs_info *info = root->fs_info;
@@ -2698,8 +2541,25 @@ static int may_rollback(struct btrfs_root *root)
physical = multi->stripes[0].physical;
kfree(multi);
- if (num_stripes != 1 || physical != bytenr)
+ if (num_stripes != 1) {
+ error("num stripes for bytenr %llu is not 1", bytenr);
goto fail;
+ }
+
+ /*
+ * Extra check for new convert, as metadata chunk from new
+ * convert is much more free than old convert, it doesn't need
+ * to do 1:1 mapping.
+ */
+ if (physical != bytenr) {
+ /*
+ * Check if it's a metadata chunk and has only metadata
+ * extent.
+ */
+ ret = may_rollback_chunk(info, bytenr);
+ if (ret < 0)
+ goto fail;
+ }
next:
bytenr += length;
if (bytenr >= total_bytes)
@@ -2863,11 +2723,19 @@ static int do_rollback(const char *devname)
num_bytes = btrfs_file_extent_num_bytes(leaf, fi);
cache1 = btrfs_lookup_block_group(root->fs_info, offset);
- cache2 = btrfs_lookup_block_group(root->fs_info,
- offset + num_bytes - 1);
- if (!cache1 || cache1 != cache2 ||
- (!(cache1->flags & BTRFS_BLOCK_GROUP_SYSTEM) &&
- !intersect_with_sb(offset, num_bytes)))
+ cache2 = btrfs_lookup_block_group(root->fs_info,
+ offset + num_bytes - 1);
+ /*
+ * Here we must take consideration of old and new convert
+ * behavior.
+ * For old convert case, sign, there is no consist chunk type
+ * that will cover the extent. META/DATA/SYS are all possible.
+ * Just ensure relocate one is in SYS chunk.
+ * For new convert case, they are all covered by DATA chunk.
+ *
+ * So, there is not valid chunk type check for it now.
+ */
+ if (cache1 != cache2)
break;
set_extent_bits(&io_tree, offset, offset + num_bytes - 1,
@@ -2881,6 +2749,7 @@ next_extent:
if (offset < total_bytes) {
fprintf(stderr, "unable to build extent mapping\n");
+ fprintf(stderr, "converted filesystem after balance is unable to rollback\n");
goto fail;
}
@@ -2957,7 +2826,7 @@ next_extent:
ret = pwrite(fd, buf, sectorsize, bytenr);
if (ret != sectorsize) {
fprintf(stderr,
- "error during zeroing supreblock %d: %d\n",
+ "error during zeroing superblock %d: %d\n",
i, ret);
goto fail;
}
diff --git a/btrfs-corrupt-block.c b/btrfs-corrupt-block.c
index 66d93e5..020afab 100644
--- a/btrfs-corrupt-block.c
+++ b/btrfs-corrupt-block.c
@@ -131,8 +131,8 @@ static void corrupt_keys(struct btrfs_trans_handle *trans,
if (nr == 0)
return;
- slot = rand() % nr;
- bad_slot = rand() % nr;
+ slot = rand_range(nr);
+ bad_slot = rand_range(nr);
if (bad_slot == slot)
return;
@@ -181,7 +181,7 @@ static int corrupt_extent(struct btrfs_trans_handle *trans,
struct btrfs_path *path;
int ret;
int slot;
- int should_del = rand() % 3;
+ int should_del = rand_range(3);
path = btrfs_alloc_path();
if (!path)
@@ -210,6 +210,7 @@ static int corrupt_extent(struct btrfs_trans_handle *trans,
break;
if (key.type != BTRFS_EXTENT_ITEM_KEY &&
+ key.type != BTRFS_METADATA_ITEM_KEY &&
key.type != BTRFS_TREE_BLOCK_REF_KEY &&
key.type != BTRFS_EXTENT_DATA_REF_KEY &&
key.type != BTRFS_EXTENT_REF_V0_KEY &&
@@ -257,7 +258,7 @@ static void btrfs_corrupt_extent_leaf(struct btrfs_trans_handle *trans,
struct extent_buffer *eb)
{
u32 nr = btrfs_header_nritems(eb);
- u32 victim = rand() % nr;
+ u32 victim = rand_range(nr);
u64 objectid;
struct btrfs_key key;
@@ -281,7 +282,7 @@ static void btrfs_corrupt_extent_tree(struct btrfs_trans_handle *trans,
}
if (btrfs_header_level(eb) == 1 && eb != root->node) {
- if (rand() % 5)
+ if (rand_range(5))
return;
}
@@ -390,7 +391,7 @@ static u64 generate_u64(u64 orig)
{
u64 ret;
do {
- ret = rand();
+ ret = rand_u64();
} while (ret == orig);
return ret;
}
@@ -399,7 +400,7 @@ static u32 generate_u32(u32 orig)
{
u32 ret;
do {
- ret = rand();
+ ret = rand_u32();
} while (ret == orig);
return ret;
}
@@ -408,7 +409,7 @@ static u8 generate_u8(u8 orig)
{
u8 ret;
do {
- ret = rand();
+ ret = rand_u8();
} while (ret == orig);
return ret;
}
@@ -719,7 +720,7 @@ static int corrupt_metadata_block(struct btrfs_root *root, u64 block,
root = btrfs_read_fs_root(root->fs_info, &root_key);
if (IS_ERR(root)) {
- fprintf(stderr, "Couldn't finde owner root %llu\n",
+ fprintf(stderr, "Couldn't find owner root %llu\n",
key.objectid);
return PTR_ERR(root);
}
@@ -871,7 +872,7 @@ static int delete_csum(struct btrfs_root *root, u64 bytenr, u64 bytes)
}
/* corrupt item using NO cow.
- * Because chunk recover will recover based on whole partition scaning,
+ * Because chunk recover will recover based on whole partition scanning,
* If using COW, chunk recover will use the old item to recover,
* which is still OK but we want to check the ability to rebuild chunk
* not only restore the old ones */
@@ -944,7 +945,7 @@ static int corrupt_chunk_tree(struct btrfs_trans_handle *trans,
while (!btrfs_previous_item(root, path, 0, BTRFS_DEV_ITEM_KEY)) {
slot = path->slots[0];
leaf = path->nodes[0];
- del = rand() % 3;
+ del = rand_range(3);
/* Never delete the first item to keep the leaf structure */
if (path->slots[0] == 0)
del = 0;
@@ -971,7 +972,7 @@ static int corrupt_chunk_tree(struct btrfs_trans_handle *trans,
while (!btrfs_previous_item(root, path, 0, BTRFS_CHUNK_ITEM_KEY)) {
slot = path->slots[0];
leaf = path->nodes[0];
- del = rand() % 3;
+ del = rand_range(3);
btrfs_item_key_to_cpu(leaf, &found_key, slot);
ret = corrupt_item_nocow(trans, root, path, del);
if (ret)
@@ -1040,7 +1041,6 @@ int main(int argc, char **argv)
char field[FIELD_BUF_LEN];
field[0] = '\0';
- srand(128);
memset(&key, 0, sizeof(key));
while(1) {
@@ -1179,7 +1179,7 @@ int main(int argc, char **argv)
if (logical == (u64)-1)
print_usage(1);
- del = rand() % 3;
+ del = rand_range(3);
path = btrfs_alloc_path();
if (!path) {
fprintf(stderr, "path allocation failed\n");
diff --git a/btrfs-crc.c b/btrfs-crc.c
index 723e0b7..c3e4774 100644
--- a/btrfs-crc.c
+++ b/btrfs-crc.c
@@ -22,15 +22,17 @@
#include "crc32c.h"
#include "utils.h"
-void usage(void)
+void print_usage(int status)
{
printf("usage: btrfs-crc filename\n");
printf(" print out the btrfs crc for \"filename\"\n");
- printf("usage: btrfs-crc filename -c crc [-s seed] [-l length]\n");
+ printf("usage: btrfs-crc -c crc [-s seed] [-l length]\n");
printf(" brute force search for file names with the given crc\n");
printf(" -s seed the random seed (default: random)\n");
printf(" -l length the length of the file names (default: 10)\n");
- exit(1);
+ printf("usage: btrfs-crc -h\n");
+ printf(" print this message\n");
+ exit(status);
}
int main(int argc, char **argv)
@@ -40,7 +42,7 @@ int main(int argc, char **argv)
char *str;
char *buf;
int length = 10;
- int seed = getpid() ^ getppid();
+ u64 seed = 0;
int loop = 0;
int i;
@@ -54,12 +56,12 @@ int main(int argc, char **argv)
loop = 1;
break;
case 's':
- seed = atol(optarg);
+ seed = atoll(optarg);
break;
case 'h':
- usage();
+ print_usage(1);
case '?':
- return 255;
+ print_usage(255);
}
}
@@ -67,21 +69,24 @@ int main(int argc, char **argv)
str = argv[optind];
if (!loop) {
- if (check_argc_min(argc - optind, 1))
- return 255;
+ if (check_argc_exact(argc - optind, 1))
+ print_usage(255);
printf("%12u - %s\n", crc32c(~1, str, strlen(str)), str);
return 0;
}
+ if (check_argc_exact(argc - optind, 0))
+ print_usage(255);
buf = malloc(length);
if (!buf)
return -ENOMEM;
- srand(seed);
+ if (seed)
+ init_rand_seed(seed);
while (1) {
for (i = 0; i < length; i++)
- buf[i] = rand() % 94 + 33;
+ buf[i] = rand_range(94) + 33;
if (crc32c(~1, buf, length) == checksum)
printf("%12lu - %.*s\n", checksum, length, buf);
}
diff --git a/btrfs-debugfs b/btrfs-debugfs
index 08e3ec6..0a654a6 100755
--- a/btrfs-debugfs
+++ b/btrfs-debugfs
@@ -96,7 +96,7 @@ class btrfs_ioctl_search_args(ctypes.Structure):
("buf", ctypes.c_ubyte * args_buffer_size),
]
-# the search ioctl resturns one header for each item
+# the search ioctl returns one header for each item
#
class btrfs_ioctl_search_header(ctypes.Structure):
_pack_ = 1
diff --git a/btrfs-fragments.c b/btrfs-fragments.c
index 9db1861..eb75eb7 100644
--- a/btrfs-fragments.c
+++ b/btrfs-fragments.c
@@ -245,7 +245,8 @@ list_fragments(int fd, u64 flags, char *dir)
sh = (struct btrfs_ioctl_search_header *)(args.buf +
off);
off += sizeof(*sh);
- if (sh->type == BTRFS_BLOCK_GROUP_ITEM_KEY) {
+ if (btrfs_search_header_type(sh)
+ == BTRFS_BLOCK_GROUP_ITEM_KEY) {
struct btrfs_block_group_item *bg;
if (im) {
@@ -262,20 +263,24 @@ list_fragments(int fd, u64 flags, char *dir)
(args.buf + off);
bgflags = btrfs_block_group_flags(bg);
bgused = btrfs_block_group_used(bg);
-
+
printf("found block group %lld len %lld "
- "flags %lld\n", sh->objectid,
- sh->offset, bgflags);
+ "flags %lld\n",
+ btrfs_search_header_objectid(sh),
+ btrfs_search_header_offset(sh),
+ bgflags);
if (!(bgflags & flags)) {
/* skip this block group */
- sk->min_objectid = sh->objectid +
- sh->offset;
+ sk->min_objectid =
+ btrfs_search_header_objectid(sh) +
+ btrfs_search_header_offset(sh);
sk->min_type = 0;
sk->min_offset = 0;
break;
}
im = gdImageCreate(width,
- (sh->offset / 4096 + 799) / width);
+ (btrfs_search_header_offset(sh)
+ / 4096 + 799) / width);
black = gdImageColorAllocate(im, 0, 0, 0);
@@ -283,8 +288,8 @@ list_fragments(int fd, u64 flags, char *dir)
colors[j] = black;
init_colors(im, colors);
- bgstart = sh->objectid;
- bglen = sh->offset;
+ bgstart = btrfs_search_header_objectid(sh);
+ bglen = btrfs_search_header_offset(sh);
bgend = bgstart + bglen;
snprintf(name, sizeof(name), "bg%d.png", bgnum);
@@ -303,7 +308,8 @@ list_fragments(int fd, u64 flags, char *dir)
areas = 0;
saved_len = 0;
}
- if (im && sh->type == BTRFS_EXTENT_ITEM_KEY) {
+ if (im && btrfs_search_header_type(sh)
+ == BTRFS_EXTENT_ITEM_KEY) {
int c;
struct btrfs_extent_item *item;
@@ -311,40 +317,48 @@ list_fragments(int fd, u64 flags, char *dir)
(args.buf + off);
if (use_color)
- c = colors[get_color(item, sh->len)];
+ c = colors[get_color(item,
+ btrfs_search_header_len(sh))];
else
c = black;
- if (sh->objectid > bgend) {
+ if (btrfs_search_header_objectid(sh) > bgend) {
printf("WARN: extent %lld is without "
- "block group\n", sh->objectid);
+ "block group\n",
+ btrfs_search_header_objectid(sh));
goto skip;
}
- if (sh->objectid == bgend) {
- saved_extent = sh->objectid;
- saved_len = sh->offset;
+ if (btrfs_search_header_objectid(sh) == bgend) {
+ saved_extent =
+ btrfs_search_header_objectid(sh);
+ saved_len =
+ btrfs_search_header_offset(sh);
saved_color = c;
goto skip;
}
- px = (sh->objectid - bgstart) / 4096;
- for (j = 0; j < sh->offset / 4096; ++j) {
+ px = (btrfs_search_header_objectid(sh)
+ - bgstart) / 4096;
+ for (j = 0;
+ j < btrfs_search_header_offset(sh) / 4096;
+ ++j) {
int x = (px + j) % width;
int y = (px + j) / width;
gdImageSetPixel(im, x, y, c);
}
- if (sh->objectid != last_end)
+ if (btrfs_search_header_objectid(sh) != last_end)
++areas;
- last_end = sh->objectid + sh->offset;
+ last_end = btrfs_search_header_objectid(sh)
+ + btrfs_search_header_offset(sh);
skip:;
}
- off += sh->len;
+ off += btrfs_search_header_len(sh);
/*
* record the mins in sk so we can make sure the
* next search doesn't repeat this root
*/
- sk->min_objectid = sh->objectid;
- sk->min_type = sh->type;
- sk->min_offset = sh->offset;
+ sk->min_objectid = btrfs_search_header_objectid(sh);
+ sk->min_type = btrfs_search_header_type(sh);
+ sk->min_offset = btrfs_search_header_offset(sh);
}
sk->nr_items = 4096;
diff --git a/btrfs-image.c b/btrfs-image.c
index 5c4c6fa..6feeb46 100644
--- a/btrfs-image.c
+++ b/btrfs-image.c
@@ -68,6 +68,13 @@ struct meta_cluster {
struct fs_chunk {
u64 logical;
u64 physical;
+ /*
+ * physical_dup only store additonal physical for BTRFS_BLOCK_GROUP_DUP
+ * currently restore only support single and DUP
+ * TODO: modify this structure and the function related to this
+ * structure for support RAID*
+ */
+ u64 physical_dup;
u64 bytes;
struct rb_node l;
struct rb_node p;
@@ -188,7 +195,7 @@ static char *generate_garbage(u32 name_len)
return NULL;
for (i = 0; i < name_len; i++) {
- char c = rand() % 94 + 33;
+ char c = rand_range(94) + 33;
if (c == '/')
c++;
@@ -290,7 +297,8 @@ static struct rb_node *tree_search(struct rb_root *root,
return NULL;
}
-static u64 logical_to_physical(struct mdrestore_struct *mdres, u64 logical, u64 *size)
+static u64 logical_to_physical(struct mdrestore_struct *mdres, u64 logical,
+ u64 *size, u64 *physical_dup)
{
struct fs_chunk *fs_chunk;
struct rb_node *entry;
@@ -312,6 +320,14 @@ static u64 logical_to_physical(struct mdrestore_struct *mdres, u64 logical, u64
BUG();
offset = search.logical - fs_chunk->logical;
+ if (physical_dup) {
+ /* Only in dup case, physical_dup is not equal to 0 */
+ if (fs_chunk->physical_dup)
+ *physical_dup = fs_chunk->physical_dup + offset;
+ else
+ *physical_dup = 0;
+ }
+
*size = min(*size, fs_chunk->bytes + fs_chunk->logical - logical);
return fs_chunk->physical + offset;
}
@@ -392,7 +408,7 @@ static char *find_collision(struct metadump_struct *md, char *name,
"generating normal garbage, it won't match indexes\n",
val->len, val->val);
for (i = 0; i < name_len; i++) {
- char c = rand() % 94 + 33;
+ char c = rand_range(94) + 33;
if (c == '/')
c++;
@@ -657,7 +673,7 @@ static void *dump_worker(void *data)
async->bufsize = compressBound(async->size);
async->buffer = malloc(async->bufsize);
if (!async->buffer) {
- fprintf(stderr, "Error allocing buffer\n");
+ fprintf(stderr, "Error allocating buffer\n");
pthread_mutex_lock(&md->mutex);
if (!md->error)
md->error = -ENOMEM;
@@ -1324,7 +1340,7 @@ static int create_metadump(const char *input, FILE *out, int num_threads,
ret = metadump_init(&metadump, root, out, num_threads,
compress_level, sanitize);
if (ret) {
- fprintf(stderr, "Error initing metadump %d\n", ret);
+ fprintf(stderr, "Error initializing metadump %d\n", ret);
close_ctree(root);
return ret;
}
@@ -1339,7 +1355,7 @@ static int create_metadump(const char *input, FILE *out, int num_threads,
path = btrfs_alloc_path();
if (!path) {
- fprintf(stderr, "Out of memory allocing path\n");
+ fprintf(stderr, "Out of memory allocating path\n");
err = -ENOMEM;
goto out;
}
@@ -1451,20 +1467,26 @@ static int update_super(struct mdrestore_struct *mdres, u8 *buffer)
cur += sizeof(*disk_key);
if (key.type == BTRFS_CHUNK_ITEM_KEY) {
- u64 physical, size = 0;
+ u64 type, physical, physical_dup, size = 0;
chunk = (struct btrfs_chunk *)ptr;
old_num_stripes = btrfs_stack_chunk_num_stripes(chunk);
chunk = (struct btrfs_chunk *)write_ptr;
memmove(write_ptr, ptr, sizeof(*chunk));
- btrfs_set_stack_chunk_num_stripes(chunk, 1);
btrfs_set_stack_chunk_sub_stripes(chunk, 0);
- btrfs_set_stack_chunk_type(chunk,
- BTRFS_BLOCK_GROUP_SYSTEM);
+ type = btrfs_stack_chunk_type(chunk);
+ if (type & BTRFS_BLOCK_GROUP_DUP) {
+ new_array_size += sizeof(struct btrfs_stripe);
+ write_ptr += sizeof(struct btrfs_stripe);
+ } else {
+ btrfs_set_stack_chunk_num_stripes(chunk, 1);
+ btrfs_set_stack_chunk_type(chunk,
+ BTRFS_BLOCK_GROUP_SYSTEM);
+ }
chunk->stripe.devid = super->dev_item.devid;
physical = logical_to_physical(mdres, key.offset,
- &size);
+ &size, &physical_dup);
if (size != (u64)-1)
btrfs_set_stack_stripe_offset(&chunk->stripe,
physical);
@@ -1573,41 +1595,47 @@ static int fixup_chunk_tree_block(struct mdrestore_struct *mdres,
goto next;
for (i = 0; i < btrfs_header_nritems(eb); i++) {
- struct btrfs_chunk chunk;
+ struct btrfs_chunk *chunk;
struct btrfs_key key;
- u64 type, physical, size = (u64)-1;
+ u64 type, physical, physical_dup, size = (u64)-1;
btrfs_item_key_to_cpu(eb, &key, i);
if (key.type != BTRFS_CHUNK_ITEM_KEY)
continue;
- truncate_item(eb, i, sizeof(chunk));
- read_extent_buffer(eb, &chunk,
- btrfs_item_ptr_offset(eb, i),
- sizeof(chunk));
size = 0;
physical = logical_to_physical(mdres, key.offset,
- &size);
+ &size, &physical_dup);
+
+ if (!physical_dup)
+ truncate_item(eb, i, sizeof(*chunk));
+ chunk = btrfs_item_ptr(eb, i, struct btrfs_chunk);
+
/* Zero out the RAID profile */
- type = btrfs_stack_chunk_type(&chunk);
+ type = btrfs_chunk_type(eb, chunk);
type &= (BTRFS_BLOCK_GROUP_DATA |
BTRFS_BLOCK_GROUP_SYSTEM |
BTRFS_BLOCK_GROUP_METADATA |
BTRFS_BLOCK_GROUP_DUP);
- btrfs_set_stack_chunk_type(&chunk, type);
+ btrfs_set_chunk_type(eb, chunk, type);
- btrfs_set_stack_chunk_num_stripes(&chunk, 1);
- btrfs_set_stack_chunk_sub_stripes(&chunk, 0);
- btrfs_set_stack_stripe_devid(&chunk.stripe, mdres->devid);
+ if (!physical_dup)
+ btrfs_set_chunk_num_stripes(eb, chunk, 1);
+ btrfs_set_chunk_sub_stripes(eb, chunk, 0);
+ btrfs_set_stripe_devid_nr(eb, chunk, 0, mdres->devid);
if (size != (u64)-1)
- btrfs_set_stack_stripe_offset(&chunk.stripe,
- physical);
- memcpy(chunk.stripe.dev_uuid, mdres->uuid,
- BTRFS_UUID_SIZE);
- write_extent_buffer(eb, &chunk,
- btrfs_item_ptr_offset(eb, i),
- sizeof(chunk));
+ btrfs_set_stripe_offset_nr(eb, chunk, 0,
+ physical);
+ /* update stripe 2 offset */
+ if (physical_dup)
+ btrfs_set_stripe_offset_nr(eb, chunk, 1,
+ physical_dup);
+
+ write_extent_buffer(eb, mdres->uuid,
+ (unsigned long)btrfs_stripe_dev_uuid_nr(
+ chunk, 0),
+ BTRFS_UUID_SIZE);
}
memcpy(buffer, eb->data, eb->len);
csum_block(buffer, eb->len);
@@ -1671,7 +1699,7 @@ static void *restore_worker(void *data)
outfd = fileno(mdres->out);
buffer = malloc(compress_size);
if (!buffer) {
- fprintf(stderr, "Error allocing buffer\n");
+ fprintf(stderr, "Error allocating buffer\n");
pthread_mutex_lock(&mdres->mutex);
if (!mdres->error)
mdres->error = -ENOMEM;
@@ -1680,7 +1708,7 @@ static void *restore_worker(void *data)
}
while (1) {
- u64 bytenr;
+ u64 bytenr, physical_dup;
off_t offset = 0;
int err = 0;
@@ -1730,29 +1758,40 @@ static void *restore_worker(void *data)
if (!mdres->fixup_offset) {
while (size) {
u64 chunk_size = size;
+ physical_dup = 0;
if (!mdres->multi_devices && !mdres->old_restore)
bytenr = logical_to_physical(mdres,
- async->start + offset,
- &chunk_size);
+ async->start + offset,
+ &chunk_size,
+ &physical_dup);
else
bytenr = async->start + offset;
ret = pwrite64(outfd, outbuf+offset, chunk_size,
bytenr);
- if (ret != chunk_size) {
- if (ret < 0) {
- fprintf(stderr, "Error writing to "
- "device %d\n", errno);
- err = errno;
- break;
- } else {
- fprintf(stderr, "Short write\n");
- err = -EIO;
- break;
- }
- }
+ if (ret != chunk_size)
+ goto error;
+
+ if (physical_dup)
+ ret = pwrite64(outfd, outbuf+offset,
+ chunk_size,
+ physical_dup);
+ if (ret != chunk_size)
+ goto error;
+
size -= chunk_size;
offset += chunk_size;
+ continue;
+
+error:
+ if (ret < 0) {
+ fprintf(stderr, "Error writing to device %d\n",
+ errno);
+ err = errno;
+ } else {
+ fprintf(stderr, "Short write\n");
+ err = -EIO;
+ }
}
} else if (async->start != BTRFS_SUPER_INFO_OFFSET) {
ret = write_data_to_disk(mdres->info, outbuf, async->start, size, 0);
@@ -1913,7 +1952,7 @@ static int add_cluster(struct meta_cluster *cluster,
async->bufsize = le32_to_cpu(item->size);
async->buffer = malloc(async->bufsize);
if (!async->buffer) {
- fprintf(stderr, "Error allocing async buffer\n");
+ fprintf(stderr, "Error allocating async buffer\n");
free(async);
return -ENOMEM;
}
@@ -2017,9 +2056,10 @@ static int read_chunk_block(struct mdrestore_struct *mdres, u8 *buffer,
}
for (i = 0; i < btrfs_header_nritems(eb); i++) {
- struct btrfs_chunk chunk;
+ struct btrfs_chunk *chunk;
struct fs_chunk *fs_chunk;
struct btrfs_key key;
+ u64 type;
if (btrfs_header_level(eb)) {
u64 blockptr = btrfs_node_blockptr(eb, i);
@@ -2038,17 +2078,16 @@ static int read_chunk_block(struct mdrestore_struct *mdres, u8 *buffer,
fs_chunk = malloc(sizeof(struct fs_chunk));
if (!fs_chunk) {
- fprintf(stderr, "Erorr allocating chunk\n");
+ fprintf(stderr, "Error allocating chunk\n");
ret = -ENOMEM;
break;
}
memset(fs_chunk, 0, sizeof(*fs_chunk));
- read_extent_buffer(eb, &chunk, btrfs_item_ptr_offset(eb, i),
- sizeof(chunk));
+ chunk = btrfs_item_ptr(eb, i, struct btrfs_chunk);
fs_chunk->logical = key.offset;
- fs_chunk->physical = btrfs_stack_stripe_offset(&chunk.stripe);
- fs_chunk->bytes = btrfs_stack_chunk_length(&chunk);
+ fs_chunk->physical = btrfs_stripe_offset_nr(eb, chunk, 0);
+ fs_chunk->bytes = btrfs_chunk_length(eb, chunk);
INIT_LIST_HEAD(&fs_chunk->list);
if (tree_search(&mdres->physical_tree, &fs_chunk->p,
physical_cmp, 1) != NULL)
@@ -2056,11 +2095,25 @@ static int read_chunk_block(struct mdrestore_struct *mdres, u8 *buffer,
else
tree_insert(&mdres->physical_tree, &fs_chunk->p,
physical_cmp);
- if (fs_chunk->physical + fs_chunk->bytes >
+
+ type = btrfs_chunk_type(eb, chunk);
+ if (type & BTRFS_BLOCK_GROUP_DUP) {
+ fs_chunk->physical_dup =
+ btrfs_stripe_offset_nr(eb, chunk, 1);
+ }
+
+ if (fs_chunk->physical_dup + fs_chunk->bytes >
+ mdres->last_physical_offset)
+ mdres->last_physical_offset = fs_chunk->physical_dup +
+ fs_chunk->bytes;
+ else if (fs_chunk->physical + fs_chunk->bytes >
mdres->last_physical_offset)
mdres->last_physical_offset = fs_chunk->physical +
fs_chunk->bytes;
mdres->alloced_chunks += fs_chunk->bytes;
+ /* in dup case, fs_chunk->bytes should add twice */
+ if (fs_chunk->physical_dup)
+ mdres->alloced_chunks += fs_chunk->bytes;
tree_insert(&mdres->chunk_tree, &fs_chunk->l, chunk_cmp);
}
out:
@@ -2090,7 +2143,7 @@ static int search_for_chunk_blocks(struct mdrestore_struct *mdres,
buffer = malloc(max_size);
if (!buffer) {
- fprintf(stderr, "Error allocing buffer\n");
+ fprintf(stderr, "Error allocating buffer\n");
free(cluster);
return -ENOMEM;
}
@@ -2098,7 +2151,7 @@ static int search_for_chunk_blocks(struct mdrestore_struct *mdres,
if (mdres->compress_method == COMPRESS_ZLIB) {
tmp = malloc(max_size);
if (!tmp) {
- fprintf(stderr, "Error allocing tmp buffer\n");
+ fprintf(stderr, "Error allocating tmp buffer\n");
free(cluster);
free(buffer);
return -ENOMEM;
@@ -2264,7 +2317,7 @@ static int build_chunk_tree(struct mdrestore_struct *mdres,
buffer = malloc(le32_to_cpu(item->size));
if (!buffer) {
- fprintf(stderr, "Error allocing buffer\n");
+ fprintf(stderr, "Error allocating buffer\n");
return -ENOMEM;
}
@@ -2360,7 +2413,7 @@ static int fixup_devices(struct btrfs_fs_info *fs_info,
path = btrfs_alloc_path();
if (!path) {
- fprintf(stderr, "Error alloc'ing path\n");
+ fprintf(stderr, "Error allocating path\n");
return -ENOMEM;
}
@@ -2490,7 +2543,7 @@ static int restore_metadump(const char *input, FILE *out, int old_restore,
ret = mdrestore_init(&mdrestore, in, out, old_restore, num_threads,
fixup_offset, info, multi_devices);
if (ret) {
- fprintf(stderr, "Error initing mdrestore %d\n", ret);
+ fprintf(stderr, "Error initializing mdrestore %d\n", ret);
goto failed_cluster;
}
diff --git a/btrfs-list.c b/btrfs-list.c
index 2da54bf..4cc2ed4 100644
--- a/btrfs-list.c
+++ b/btrfs-list.c
@@ -564,7 +564,7 @@ static int resolve_root(struct root_lookup *rl, struct root_info *ri,
int add_len;
/*
- * ref_tree = 0 indicates the subvolumes
+ * ref_tree = 0 indicates the subvolume
* has been deleted.
*/
if (!found->ref_tree) {
@@ -678,7 +678,7 @@ static int lookup_ino_path(int fd, struct root_info *ri)
}
/* finding the generation for a given path is a two step process.
- * First we use the inode loookup routine to find out the root id
+ * First we use the inode lookup routine to find out the root id
*
* Then we use the tree search ioctl to scan all the root items for a
* given root id and spit out the latest generation we can find
@@ -876,9 +876,9 @@ static char *ino_resolve(int fd, u64 ino, u64 *cache_dirid, char **cache_name)
off = 0;
sh = (struct btrfs_ioctl_search_header *)(args.buf + off);
- if (sh->type == BTRFS_INODE_REF_KEY) {
+ if (btrfs_search_header_type(sh) == BTRFS_INODE_REF_KEY) {
struct btrfs_inode_ref *ref;
- dirid = sh->offset;
+ dirid = btrfs_search_header_offset(sh);
ref = (struct btrfs_inode_ref *)(sh + 1);
namelen = btrfs_stack_inode_ref_name_len(ref);
@@ -947,7 +947,7 @@ int btrfs_list_get_default_subvolume(int fd, u64 *default_id)
sh = (struct btrfs_ioctl_search_header *)args.buf;
- if (sh->type == BTRFS_DIR_ITEM_KEY) {
+ if (btrfs_search_header_type(sh) == BTRFS_DIR_ITEM_KEY) {
struct btrfs_dir_item *di;
int name_len;
char *name;
@@ -1602,17 +1602,18 @@ static int print_one_extent(int fd, struct btrfs_ioctl_search_header *sh,
int flags = 0;
char *name = NULL;
- if (sh->objectid == *cache_ino) {
+ if (btrfs_search_header_objectid(sh) == *cache_ino) {
name = *cache_full_name;
} else if (*cache_full_name) {
free(*cache_full_name);
*cache_full_name = NULL;
}
if (!name) {
- name = ino_resolve(fd, sh->objectid, cache_dirid,
+ name = ino_resolve(fd, btrfs_search_header_objectid(sh),
+ cache_dirid,
cache_dir_name);
*cache_full_name = name;
- *cache_ino = sh->objectid;
+ *cache_ino = btrfs_search_header_objectid(sh);
}
if (!name)
return -EIO;
@@ -1633,16 +1634,16 @@ static int print_one_extent(int fd, struct btrfs_ioctl_search_header *sh,
printf("unhandled extent type %d for inode %llu "
"file offset %llu gen %llu\n",
type,
- (unsigned long long)sh->objectid,
- (unsigned long long)sh->offset,
+ (unsigned long long)btrfs_search_header_objectid(sh),
+ (unsigned long long)btrfs_search_header_offset(sh),
(unsigned long long)found_gen);
return -EIO;
}
printf("inode %llu file offset %llu len %llu disk start %llu "
"offset %llu gen %llu flags ",
- (unsigned long long)sh->objectid,
- (unsigned long long)sh->offset,
+ (unsigned long long)btrfs_search_header_objectid(sh),
+ (unsigned long long)btrfs_search_header_offset(sh),
(unsigned long long)len,
(unsigned long long)disk_start,
(unsigned long long)disk_offset,
diff --git a/chunk-recover.c b/chunk-recover.c
index 4ad337b..085e9a2 100644
--- a/chunk-recover.c
+++ b/chunk-recover.c
@@ -247,7 +247,7 @@ again:
generation);
/*
* According to the current kernel code, the following
- * case is impossble, or there is something wrong in
+ * case is impossible, or there is something wrong in
* the kernel code.
*/
if (memcmp(((void *)exist) + offset,
@@ -618,7 +618,7 @@ static int check_chunk_by_metadata(struct recover_control *rc,
btrfs_dev_extent_chunk_offset(l, dev_extent)) {
if (rc->verbose)
fprintf(stderr,
- "Device tree unmatch with chunks dev_extent[%llu, %llu], chunk[%llu, %llu]\n",
+ "Device tree mismatch with chunks dev_extent[%llu, %llu], chunk[%llu, %llu]\n",
btrfs_dev_extent_chunk_offset(l,
dev_extent),
btrfs_dev_extent_length(l, dev_extent),
@@ -654,7 +654,7 @@ bg_check:
if (chunk->type_flags != btrfs_disk_block_group_flags(l, bg_ptr)) {
if (rc->verbose)
fprintf(stderr,
- "Chunk[%llu, %llu]'s type(%llu) is differemt with Block Group's type(%llu)\n",
+ "Chunk[%llu, %llu]'s type(%llu) is different with Block Group's type(%llu)\n",
chunk->offset, chunk->length, chunk->type_flags,
btrfs_disk_block_group_flags(l, bg_ptr));
btrfs_release_path(&path);
diff --git a/cmds-balance.c b/cmds-balance.c
index 5f05f60..708bbf4 100644
--- a/cmds-balance.c
+++ b/cmds-balance.c
@@ -160,7 +160,7 @@ static int parse_range_strict(const char *range, u64 *start, u64 *end)
}
/*
- * Convert 64bit range to 32bit with boundary checkso
+ * Convert 64bit range to 32bit with boundary checks
*/
static int range_to_u32(u64 start, u64 end, u32 *start32, u32 *end32)
{
@@ -445,9 +445,9 @@ static int do_balance(const char *path, struct btrfs_ioctl_balance_args *args,
printf("\twarning. The operation will start in %d seconds.\n", delay);
printf("\tUse Ctrl-C to stop it.\n");
while (delay) {
- sleep(1);
printf("%2d", delay--);
fflush(stdout);
+ sleep(1);
}
printf("\nStarting balance without any filters.\n");
}
@@ -876,7 +876,7 @@ static int cmd_balance_full(int argc, char **argv)
}
static const char balance_cmd_group_info[] =
-"balance data accross devices, or change block groups using filters";
+"balance data across devices, or change block groups using filters";
const struct cmd_group balance_cmd_group = {
balance_cmd_group_usage, balance_cmd_group_info, {
diff --git a/cmds-check.c b/cmds-check.c
index 127ac97..9927fce 100644
--- a/cmds-check.c
+++ b/cmds-check.c
@@ -122,6 +122,9 @@ struct tree_backref {
};
};
+/* Explicit initialization for extent_record::flag_block_full_backref */
+enum { FLAG_UNSET = 2 };
+
struct extent_record {
struct list_head backrefs;
struct list_head dups;
@@ -138,7 +141,7 @@ struct extent_record {
u64 info_objectid;
u32 num_duplicates;
u8 info_level;
- int flag_block_full_backref;
+ unsigned int flag_block_full_backref:2;
unsigned int found_rec:1;
unsigned int content_checked:1;
unsigned int owner_ref_checked:1;
@@ -476,7 +479,7 @@ static int del_file_extent_hole(struct rb_root *holes,
return -EEXIST;
/*
- * Now there will be no overflap, delete the hole and re-add the
+ * Now there will be no overlap, delete the hole and re-add the
* split(s) if they exists.
*/
if (start > hole->start) {
@@ -746,9 +749,9 @@ static void print_ref_error(int errors)
if (errors & REF_ERR_DUP_INODE_REF)
fprintf(stderr, ", dup inode ref");
if (errors & REF_ERR_INDEX_UNMATCH)
- fprintf(stderr, ", index unmatch");
+ fprintf(stderr, ", index mismatch");
if (errors & REF_ERR_FILETYPE_UNMATCH)
- fprintf(stderr, ", filetype unmatch");
+ fprintf(stderr, ", filetype mismatch");
if (errors & REF_ERR_NAME_TOO_LONG)
fprintf(stderr, ", name too long");
if (errors & REF_ERR_NO_ROOT_REF)
@@ -2689,7 +2692,7 @@ static int repair_inode_no_item(struct btrfs_trans_handle *trans,
type_recovered = 1;
filetype = BTRFS_FT_REG_FILE;
} else{
- printf("Can't determint the filetype for inode %llu, assume it is a normal file\n",
+ printf("Can't determine the filetype for inode %llu, assume it is a normal file\n",
rec->ino);
type_recovered = 1;
filetype = BTRFS_FT_REG_FILE;
@@ -2892,7 +2895,7 @@ static int check_inode_recs(struct btrfs_root *root,
/*
* We need to record the highest inode number for later 'lost+found'
* dir creation.
- * We must select a ino not used/refered by any existing inode, or
+ * We must select an ino not used/referred by any existing inode, or
* 'lost+found' ino may be a missing ino in a corrupted leaf,
* this may cause 'lost+found' dir has wrong nlinks.
*/
@@ -4323,7 +4326,7 @@ static int check_block(struct btrfs_root *root,
} else {
/*
* Signal to callers we need to start the scan over
- * again since we'll have cow'ed blocks.
+ * again since we'll have cowed blocks.
*/
ret = -EAGAIN;
}
@@ -4509,11 +4512,13 @@ static void check_extent_type(struct extent_record *rec)
}
}
+/*
+ * Allocate a new extent record, fill default values from @tmpl and insert int
+ * @extent_cache. Caller is supposed to make sure the [start,nr) is not in
+ * the cache, otherwise it fails.
+ */
static int add_extent_rec_nolookup(struct cache_tree *extent_cache,
- struct btrfs_key *parent_key, u64 parent_gen,
- u64 start, u64 nr, u64 extent_item_refs,
- int is_root, int inc_ref, int set_checked,
- int metadata, int extent_rec, u64 max_size)
+ struct extent_record *tmpl)
{
struct extent_record *rec;
int ret = 0;
@@ -4521,90 +4526,72 @@ static int add_extent_rec_nolookup(struct cache_tree *extent_cache,
rec = malloc(sizeof(*rec));
if (!rec)
return -ENOMEM;
- rec->start = start;
- rec->max_size = max_size;
- rec->nr = max(nr, max_size);
- rec->found_rec = !!extent_rec;
- rec->content_checked = 0;
- rec->owner_ref_checked = 0;
+ rec->start = tmpl->start;
+ rec->max_size = tmpl->max_size;
+ rec->nr = max(tmpl->nr, tmpl->max_size);
+ rec->found_rec = tmpl->found_rec;
+ rec->content_checked = tmpl->content_checked;
+ rec->owner_ref_checked = tmpl->owner_ref_checked;
rec->num_duplicates = 0;
- rec->metadata = metadata;
- rec->flag_block_full_backref = -1;
+ rec->metadata = tmpl->metadata;
+ rec->flag_block_full_backref = FLAG_UNSET;
rec->bad_full_backref = 0;
rec->crossing_stripes = 0;
rec->wrong_chunk_type = 0;
+ rec->is_root = tmpl->is_root;
+ rec->refs = tmpl->refs;
+ rec->extent_item_refs = tmpl->extent_item_refs;
+ rec->parent_generation = tmpl->parent_generation;
INIT_LIST_HEAD(&rec->backrefs);
INIT_LIST_HEAD(&rec->dups);
INIT_LIST_HEAD(&rec->list);
-
- if (is_root)
- rec->is_root = 1;
- else
- rec->is_root = 0;
-
- if (inc_ref)
- rec->refs = 1;
- else
- rec->refs = 0;
-
- if (extent_item_refs)
- rec->extent_item_refs = extent_item_refs;
- else
- rec->extent_item_refs = 0;
-
- if (parent_key)
- btrfs_cpu_key_to_disk(&rec->parent_key, parent_key);
- else
- memset(&rec->parent_key, 0, sizeof(*parent_key));
-
- if (parent_gen)
- rec->parent_generation = parent_gen;
- else
- rec->parent_generation = 0;
-
- rec->cache.start = start;
- rec->cache.size = nr;
+ memcpy(&rec->parent_key, &tmpl->parent_key, sizeof(tmpl->parent_key));
+ rec->cache.start = tmpl->start;
+ rec->cache.size = tmpl->nr;
ret = insert_cache_extent(extent_cache, &rec->cache);
BUG_ON(ret);
- bytes_used += nr;
- if (set_checked) {
- rec->content_checked = 1;
- rec->owner_ref_checked = 1;
- }
+ bytes_used += rec->nr;
- if (metadata)
+ if (tmpl->metadata)
rec->crossing_stripes = check_crossing_stripes(rec->start,
- rec->max_size);
+ global_info->tree_root->nodesize);
check_extent_type(rec);
return ret;
}
+/*
+ * Lookup and modify an extent, some values of @tmpl are interpreted verbatim,
+ * some are hints:
+ * - refs - if found, increase refs
+ * - is_root - if found, set
+ * - content_checked - if found, set
+ * - owner_ref_checked - if found, set
+ *
+ * If not found, create a new one, initialize and insert.
+ */
static int add_extent_rec(struct cache_tree *extent_cache,
- struct btrfs_key *parent_key, u64 parent_gen,
- u64 start, u64 nr, u64 extent_item_refs,
- int is_root, int inc_ref, int set_checked,
- int metadata, int extent_rec, u64 max_size)
+ struct extent_record *tmpl)
{
struct extent_record *rec;
struct cache_extent *cache;
int ret = 0;
int dup = 0;
- cache = lookup_cache_extent(extent_cache, start, nr);
+ cache = lookup_cache_extent(extent_cache, tmpl->start, tmpl->nr);
if (cache) {
rec = container_of(cache, struct extent_record, cache);
- if (inc_ref)
+ if (tmpl->refs)
rec->refs++;
if (rec->nr == 1)
- rec->nr = max(nr, max_size);
+ rec->nr = max(tmpl->nr, tmpl->max_size);
/*
* We need to make sure to reset nr to whatever the extent
* record says was the real size, this way we can compare it to
* the backrefs.
*/
- if (extent_rec) {
- if (start != rec->start || rec->found_rec) {
+ if (tmpl->found_rec) {
+ if (tmpl->start != rec->start || rec->found_rec) {
struct extent_record *tmp;
dup = 1;
@@ -4621,46 +4608,44 @@ static int add_extent_rec(struct cache_tree *extent_cache,
tmp = malloc(sizeof(*tmp));
if (!tmp)
return -ENOMEM;
- tmp->start = start;
- tmp->max_size = max_size;
- tmp->nr = nr;
+ tmp->start = tmpl->start;
+ tmp->max_size = tmpl->max_size;
+ tmp->nr = tmpl->nr;
tmp->found_rec = 1;
- tmp->metadata = metadata;
- tmp->extent_item_refs = extent_item_refs;
+ tmp->metadata = tmpl->metadata;
+ tmp->extent_item_refs = tmpl->extent_item_refs;
INIT_LIST_HEAD(&tmp->list);
list_add_tail(&tmp->list, &rec->dups);
rec->num_duplicates++;
} else {
- rec->nr = nr;
+ rec->nr = tmpl->nr;
rec->found_rec = 1;
}
}
- if (extent_item_refs && !dup) {
+ if (tmpl->extent_item_refs && !dup) {
if (rec->extent_item_refs) {
fprintf(stderr, "block %llu rec "
"extent_item_refs %llu, passed %llu\n",
- (unsigned long long)start,
+ (unsigned long long)tmpl->start,
(unsigned long long)
rec->extent_item_refs,
- (unsigned long long)extent_item_refs);
+ (unsigned long long)tmpl->extent_item_refs);
}
- rec->extent_item_refs = extent_item_refs;
+ rec->extent_item_refs = tmpl->extent_item_refs;
}
- if (is_root)
+ if (tmpl->is_root)
rec->is_root = 1;
- if (set_checked) {
+ if (tmpl->content_checked)
rec->content_checked = 1;
+ if (tmpl->owner_ref_checked)
rec->owner_ref_checked = 1;
- }
-
- if (parent_key)
- btrfs_cpu_key_to_disk(&rec->parent_key, parent_key);
- if (parent_gen)
- rec->parent_generation = parent_gen;
-
- if (rec->max_size < max_size)
- rec->max_size = max_size;
+ memcpy(&rec->parent_key, &tmpl->parent_key,
+ sizeof(tmpl->parent_key));
+ if (tmpl->parent_generation)
+ rec->parent_generation = tmpl->parent_generation;
+ if (rec->max_size < tmpl->max_size)
+ rec->max_size = tmpl->max_size;
/*
* A metadata extent can't cross stripe_len boundary, otherwise
@@ -4668,17 +4653,15 @@ static int add_extent_rec(struct cache_tree *extent_cache,
* As now stripe_len is fixed to BTRFS_STRIPE_LEN, just check
* it.
*/
- if (metadata)
+ if (tmpl->metadata)
rec->crossing_stripes = check_crossing_stripes(
- rec->start, rec->max_size);
+ rec->start, global_info->tree_root->nodesize);
check_extent_type(rec);
maybe_free_extent_rec(extent_cache, rec);
return ret;
}
- ret = add_extent_rec_nolookup(extent_cache, parent_key, parent_gen,
- start, nr, extent_item_refs, is_root, inc_ref,
- set_checked, metadata, extent_rec, max_size);
+ ret = add_extent_rec_nolookup(extent_cache, tmpl);
return ret;
}
@@ -4692,8 +4675,15 @@ static int add_tree_backref(struct cache_tree *extent_cache, u64 bytenr,
cache = lookup_cache_extent(extent_cache, bytenr, 1);
if (!cache) {
- add_extent_rec_nolookup(extent_cache, NULL, 0, bytenr,
- 1, 0, 0, 0, 0, 1, 0, 0);
+ struct extent_record tmpl;
+
+ memset(&tmpl, 0, sizeof(tmpl));
+ tmpl.start = bytenr;
+ tmpl.nr = 1;
+ tmpl.metadata = 1;
+
+ add_extent_rec_nolookup(extent_cache, &tmpl);
+
cache = lookup_cache_extent(extent_cache, bytenr, 1);
if (!cache)
abort();
@@ -4744,8 +4734,15 @@ static int add_data_backref(struct cache_tree *extent_cache, u64 bytenr,
cache = lookup_cache_extent(extent_cache, bytenr, 1);
if (!cache) {
- add_extent_rec_nolookup(extent_cache, NULL, 0, bytenr, 1, 0, 0,
- 0, 0, 0, 0, max_size);
+ struct extent_record tmpl;
+
+ memset(&tmpl, 0, sizeof(tmpl));
+ tmpl.start = bytenr;
+ tmpl.nr = 1;
+ tmpl.max_size = max_size;
+
+ add_extent_rec_nolookup(extent_cache, &tmpl);
+
cache = lookup_cache_extent(extent_cache, bytenr, 1);
if (!cache)
abort();
@@ -5210,6 +5207,7 @@ static int process_extent_item(struct btrfs_root *root,
struct btrfs_extent_data_ref *dref;
struct btrfs_shared_data_ref *sref;
struct btrfs_key key;
+ struct extent_record tmpl;
unsigned long end;
unsigned long ptr;
int type;
@@ -5237,9 +5235,15 @@ static int process_extent_item(struct btrfs_root *root,
#else
BUG();
#endif
- return add_extent_rec(extent_cache, NULL, 0, key.objectid,
- num_bytes, refs, 0, 0, 0, metadata, 1,
- num_bytes);
+ memset(&tmpl, 0, sizeof(tmpl));
+ tmpl.start = key.objectid;
+ tmpl.nr = num_bytes;
+ tmpl.extent_item_refs = refs;
+ tmpl.metadata = metadata;
+ tmpl.found_rec = 1;
+ tmpl.max_size = num_bytes;
+
+ return add_extent_rec(extent_cache, &tmpl);
}
ei = btrfs_item_ptr(eb, slot, struct btrfs_extent_item);
@@ -5249,8 +5253,14 @@ static int process_extent_item(struct btrfs_root *root,
else
metadata = 0;
- add_extent_rec(extent_cache, NULL, 0, key.objectid, num_bytes,
- refs, 0, 0, 0, metadata, 1, num_bytes);
+ memset(&tmpl, 0, sizeof(tmpl));
+ tmpl.start = key.objectid;
+ tmpl.nr = num_bytes;
+ tmpl.extent_item_refs = refs;
+ tmpl.metadata = metadata;
+ tmpl.found_rec = 1;
+ tmpl.max_size = num_bytes;
+ add_extent_rec(extent_cache, &tmpl);
ptr = (unsigned long)(ei + 1);
if (btrfs_extent_flags(eb, ei) & BTRFS_EXTENT_FLAG_TREE_BLOCK &&
@@ -5539,7 +5549,7 @@ static int check_space_cache(struct btrfs_root *root)
ret = verify_space_cache(root, cache);
if (ret) {
- fprintf(stderr, "cache appears valid but isnt %Lu\n",
+ fprintf(stderr, "cache appears valid but isn't %Lu\n",
cache->key.objectid);
error++;
}
@@ -5629,7 +5639,7 @@ static int check_extent_exists(struct btrfs_root *root, u64 bytenr,
path = btrfs_alloc_path();
if (!path) {
- fprintf(stderr, "Error allocing path\n");
+ fprintf(stderr, "Error allocating path\n");
return -ENOMEM;
}
@@ -5662,7 +5672,7 @@ again:
/*
* Block group items come before extent items if they have the same
- * bytenr, so walk back one more just in case. Dear future traveler,
+ * bytenr, so walk back one more just in case. Dear future traveller,
* first congrats on mastering time travel. Now if it's not too much
* trouble could you go back to 2006 and tell Chris to make the
* BLOCK_GROUP_ITEM_KEY (and BTRFS_*_REF_KEY) lower than the
@@ -5871,7 +5881,7 @@ static int is_dropped_key(struct btrfs_key *key,
* 1) If BTRFS_HEADER_FLAG_RELOC is set then we have FULL_BACKREF set.
* 2) If btrfs_header_owner(buf) no longer points to buf then we have
* FULL_BACKREF set.
- * 3) We cow'ed the block walking down a reloc tree. This is impossible to tell
+ * 3) We cowed the block walking down a reloc tree. This is impossible to tell
* if it happened after the relocation occurred since we'll have dropped the
* reloc root, so it's entirely possible to have FULL_BACKREF set on buf and
* have no real way to know for sure.
@@ -5925,13 +5935,13 @@ static int calc_extent_flag(struct btrfs_root *root,
goto full_backref;
normal:
*flags = 0;
- if (rec->flag_block_full_backref != -1 &&
+ if (rec->flag_block_full_backref != FLAG_UNSET &&
rec->flag_block_full_backref != 0)
rec->bad_full_backref = 1;
return 0;
full_backref:
*flags |= BTRFS_BLOCK_FLAG_FULL_BACKREF;
- if (rec->flag_block_full_backref != -1 &&
+ if (rec->flag_block_full_backref != FLAG_UNSET &&
rec->flag_block_full_backref != 1)
rec->bad_full_backref = 1;
return 0;
@@ -6213,6 +6223,8 @@ static int run_next_block(struct btrfs_root *root,
btrfs_item_key_to_cpu(buf, &first_key, 0);
level = btrfs_header_level(buf);
for (i = 0; i < nritems; i++) {
+ struct extent_record tmpl;
+
ptr = btrfs_node_blockptr(buf, i);
size = root->nodesize;
btrfs_node_key_to_cpu(buf, &key, i);
@@ -6222,10 +6234,16 @@ static int run_next_block(struct btrfs_root *root,
continue;
}
}
- ret = add_extent_rec(extent_cache, &key,
- btrfs_node_ptr_generation(buf, i),
- ptr, size, 0, 0, 1, 0, 1, 0,
- size);
+
+ memset(&tmpl, 0, sizeof(tmpl));
+ btrfs_cpu_key_to_disk(&tmpl.parent_key, &key);
+ tmpl.parent_generation = btrfs_node_ptr_generation(buf, i);
+ tmpl.start = ptr;
+ tmpl.nr = size;
+ tmpl.refs = 1;
+ tmpl.metadata = 1;
+ tmpl.max_size = size;
+ ret = add_extent_rec(extent_cache, &tmpl);
BUG_ON(ret);
add_tree_backref(extent_cache, ptr, parent, owner, 1);
@@ -6261,12 +6279,21 @@ static int add_root_to_pending(struct extent_buffer *buf,
struct cache_tree *nodes,
u64 objectid)
{
+ struct extent_record tmpl;
+
if (btrfs_header_level(buf) > 0)
add_pending(nodes, seen, buf->start, buf->len);
else
add_pending(pending, seen, buf->start, buf->len);
- add_extent_rec(extent_cache, NULL, 0, buf->start, buf->len,
- 0, 1, 1, 0, 1, 0, buf->len);
+
+ memset(&tmpl, 0, sizeof(tmpl));
+ tmpl.start = buf->start;
+ tmpl.nr = buf->len;
+ tmpl.is_root = 1;
+ tmpl.refs = 1;
+ tmpl.metadata = 1;
+ tmpl.max_size = buf->len;
+ add_extent_rec(extent_cache, &tmpl);
if (objectid == BTRFS_TREE_RELOC_OBJECTID ||
btrfs_header_backref_rev(buf) < BTRFS_MIXED_BACKREF_REV)
@@ -7063,7 +7090,7 @@ static int delete_duplicate_records(struct btrfs_root *root,
if (tmp->start + tmp->nr < good->start + good->nr) {
fprintf(stderr, "Ok we have overlapping extents that "
- "aren't completely covered by eachother, this "
+ "aren't completely covered by each other, this "
"is going to require more careful thought. "
"The extents are [%Lu-%Lu] and [%Lu-%Lu]\n",
tmp->start, tmp->nr, good->start, good->nr);
@@ -8797,7 +8824,7 @@ static int reinit_extent_tree(struct btrfs_trans_handle *trans,
ret = reset_balance(trans, fs_info);
if (ret)
- fprintf(stderr, "error reseting the pending balance\n");
+ fprintf(stderr, "error resetting the pending balance\n");
return ret;
}
@@ -9479,8 +9506,8 @@ out:
const char * const cmd_check_usage[] = {
"btrfs check [options] <device>",
- "Check structural inegrity of a filesystem (unmounted).",
- "Check structural inegrity of an unmounted filesystem. Verify internal",
+ "Check structural integrity of a filesystem (unmounted).",
+ "Check structural integrity of an unmounted filesystem. Verify internal",
"trees' consistency and item connectivity. In the repair mode try to",
"fix the problems found.",
"WARNING: the repair mode is considered dangerous",
@@ -9491,7 +9518,7 @@ const char * const cmd_check_usage[] = {
"--readonly run in read-only mode (default)",
"--init-csum-tree create a new CRC tree",
"--init-extent-tree create a new extent tree",
- "--check-data-csum verify checkums of data blocks",
+ "--check-data-csum verify checksums of data blocks",
"-Q|--qgroup-report print a report on qgroup consistency",
"-E|--subvol-extents <subvolid>",
" print subvolume extents and sharing state",
@@ -9863,6 +9890,7 @@ out:
(unsigned long long)data_bytes_allocated,
(unsigned long long)data_bytes_referenced);
+ free_qgroup_counts();
free_root_recs_tree(&root_cache);
close_out:
close_ctree(root);
diff --git a/cmds-fi-du.c b/cmds-fi-du.c
index 168fc72..12855a5 100644
--- a/cmds-fi-du.c
+++ b/cmds-fi-du.c
@@ -83,7 +83,7 @@ static int add_shared_extent(u64 start, u64 len, struct rb_root *root)
sh = calloc(1, sizeof(*sh));
if (!sh)
- return ENOMEM;
+ return -ENOMEM;
sh->start = start;
sh->last = (start + len - 1);
@@ -153,7 +153,7 @@ static u64 count_unique_bytes(struct rb_root *root, struct shared_extent *n)
/*
* What we want to do here is get a count of shared bytes within the
- * set of extents we have collected. Specifcally, we don't want to
+ * set of extents we have collected. Specifically, we don't want to
* count any byte more than once, so just adding them up doesn't
* work.
*
@@ -233,7 +233,7 @@ static int mark_inode_seen(u64 ino, u64 subvol)
si = calloc(1, sizeof(*si));
if (!si)
- return ENOMEM;
+ return -ENOMEM;
si->i_ino = ino;
si->i_subvol = subvol;
@@ -259,7 +259,7 @@ static int inode_seen(u64 ino, u64 subvol)
else if (cmp > 0)
n = n->rb_right;
else
- return EEXIST;
+ return -EEXIST;
}
return 0;
}
@@ -308,7 +308,7 @@ static int du_calc_file_space(int fd, struct rb_root *shared_extents,
fiemap->fm_extent_count = count;
rc = ioctl(fd, FS_IOC_FIEMAP, (unsigned long) fiemap);
if (rc < 0) {
- ret = errno;
+ ret = -errno;
goto out;
}
@@ -418,17 +418,15 @@ static int du_add_file(const char *filename, int dirfd,
DIR *dirstream = NULL;
ret = fstatat(dirfd, filename, &st, 0);
- if (ret) {
- ret = errno;
- return ret;
- }
+ if (ret)
+ return -errno;
if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode))
return 0;
if (len > (path_max - pathp)) {
error("path too long: %s %s", path, filename);
- return ENAMETOOLONG;
+ return -ENAMETOOLONG;
}
pathtmp = pathp;
@@ -440,7 +438,7 @@ static int du_add_file(const char *filename, int dirfd,
fd = open_file_or_dir3(path, &dirstream, O_RDONLY);
if (fd < 0) {
- ret = fd;
+ ret = -errno;
goto out;
}
@@ -568,7 +566,7 @@ int cmd_filesystem_du(int argc, char **argv)
ret = du_add_file(argv[i], AT_FDCWD, NULL, NULL, NULL, 1);
if (ret) {
error("cannot check space of '%s': %s", argv[i],
- strerror(ret));
+ strerror(-ret));
err = 1;
}
diff --git a/cmds-fi-usage.c b/cmds-fi-usage.c
index 33bf403..b26ece1 100644
--- a/cmds-fi-usage.c
+++ b/cmds-fi-usage.c
@@ -185,11 +185,11 @@ static int load_chunk_info(int fd, struct chunk_info **info_ptr, int *info_count
return 1;
}
- off += sh->len;
+ off += btrfs_search_header_len(sh);
- sk->min_objectid = sh->objectid;
- sk->min_type = sh->type;
- sk->min_offset = sh->offset+1;
+ sk->min_objectid = btrfs_search_header_objectid(sh);
+ sk->min_type = btrfs_search_header_type(sh);
+ sk->min_offset = btrfs_search_header_offset(sh)+1;
}
if (!sk->min_offset) /* overflow */
@@ -280,7 +280,7 @@ static struct btrfs_ioctl_space_args *load_space_info(int fd, char *path)
}
/*
- * This function computes the space occuped by a *single* RAID5/RAID6 chunk.
+ * This function computes the space occupied by a *single* RAID5/RAID6 chunk.
* The computation is performed on the basis of the number of stripes
* which compose the chunk, which could be different from the number of devices
* if a disk is added later.
@@ -1024,13 +1024,8 @@ void print_device_sizes(int fd, struct device_info *devinfo, unsigned unit_mode)
printf(" Device size: %*s%10s\n",
(int)(20 - strlen("Device size")), "",
pretty_size_mode(devinfo->device_size, unit_mode));
-#if 0
- /*
- * The term has not seen an agreement and we don't want to change it
- * once it's in non-development branches or even released.
- */
- printf(" FS occupied: %*s%10s\n",
- (int)(20 - strlen("FS occupied")), "",
- pretty_size_mode(devinfo->size, unit_mode));
-#endif
+ printf(" Device slack: %*s%10s\n",
+ (int)(20 - strlen("Device slack")), "",
+ pretty_size_mode(devinfo->device_size - devinfo->size,
+ unit_mode));
}
diff --git a/cmds-filesystem.c b/cmds-filesystem.c
index e27cb26..9392a30 100644
--- a/cmds-filesystem.c
+++ b/cmds-filesystem.c
@@ -898,9 +898,10 @@ devs_only:
list_for_each_entry(fs_devices, &all_uuids, list)
print_one_uuid(fs_devices, unit_mode);
- if (search && !found)
+ if (search && !found) {
+ error("not a valid btrfs filesystem: %s", search);
ret = 1;
-
+ }
while (!list_empty(&all_uuids)) {
fs_devices = list_entry(all_uuids.next,
struct btrfs_fs_devices, list);
@@ -1184,7 +1185,7 @@ static int cmd_filesystem_resize(int argc, char **argv)
DIR *dirstream = NULL;
struct stat st;
- clean_args_no_options(argc, argv, cmd_filesystem_resize_usage);
+ clean_args_no_options_relaxed(argc, argv, cmd_filesystem_resize_usage);
if (check_argc_exact(argc - optind, 2))
usage(cmd_filesystem_resize_usage);
diff --git a/cmds-inspect-tree-stats.c b/cmds-inspect-tree-stats.c
index e80041d..c35f588 100644
--- a/cmds-inspect-tree-stats.c
+++ b/cmds-inspect-tree-stats.c
@@ -496,7 +496,7 @@ int cmd_inspect_tree_stats(int argc, char **argv)
key.objectid = BTRFS_FS_TREE_OBJECTID;
key.offset = (u64)-1;
- printf("Calculatin' size of fs tree\n");
+ printf("Calculating size of fs tree\n");
ret = calc_root_size(root, &key, 1);
if (ret)
goto out;
diff --git a/cmds-inspect.c b/cmds-inspect.c
index abe4edf..dd7b9dd 100644
--- a/cmds-inspect.c
+++ b/cmds-inspect.c
@@ -415,7 +415,7 @@ static void adjust_dev_min_size(struct list_head *extents,
/*
* List of device extents is sorted by descending order of the extent's
* end offset. If some extent goes beyond the computed minimum size,
- * which initially matches the sum of the lenghts of all extents,
+ * which initially matches the sum of the lengths of all extents,
* we need to check if the extent can be relocated to an hole in the
* device between [0, *min_size[ (which is what the resize ioctl does).
*/
@@ -537,32 +537,33 @@ static int print_min_dev_size(int fd, u64 devid)
off);
off += sizeof(*sh);
extent = (struct btrfs_dev_extent *)(args.buf + off);
- off += sh->len;
+ off += btrfs_search_header_len(sh);
- sk->min_objectid = sh->objectid;
- sk->min_type = sh->type;
- sk->min_offset = sh->offset + 1;
+ sk->min_objectid = btrfs_search_header_objectid(sh);
+ sk->min_type = btrfs_search_header_type(sh);
+ sk->min_offset = btrfs_search_header_offset(sh) + 1;
- if (sh->objectid != devid ||
- sh->type != BTRFS_DEV_EXTENT_KEY)
+ if (btrfs_search_header_objectid(sh) != devid ||
+ btrfs_search_header_type(sh) != BTRFS_DEV_EXTENT_KEY)
continue;
len = btrfs_stack_dev_extent_length(extent);
min_size += len;
- ret = add_dev_extent(&extents, sh->offset,
- sh->offset + len - 1, 0);
+ ret = add_dev_extent(&extents,
+ btrfs_search_header_offset(sh),
+ btrfs_search_header_offset(sh) + len - 1, 0);
if (!ret && last_pos != (u64)-1 &&
- last_pos != sh->offset)
+ last_pos != btrfs_search_header_offset(sh))
ret = add_dev_extent(&holes, last_pos,
- sh->offset - 1, 1);
+ btrfs_search_header_offset(sh) - 1, 1);
if (ret) {
error("add device extent: %s", strerror(-ret));
ret = 1;
goto out;
}
- last_pos = sh->offset + len;
+ last_pos = btrfs_search_header_offset(sh) + len;
}
if (sk->min_type != BTRFS_DEV_EXTENT_KEY ||
diff --git a/cmds-property.c b/cmds-property.c
index 48a8945..e59882b 100644
--- a/cmds-property.c
+++ b/cmds-property.c
@@ -298,7 +298,7 @@ static void parse_args(int argc, char **argv,
{
int ret;
char *type_str = NULL;
- int max_nonopt_args = 0;
+ int max_nonopt_args = 1;
optind = 1;
while (1) {
@@ -315,8 +315,6 @@ static void parse_args(int argc, char **argv,
}
}
- if (object)
- max_nonopt_args++;
if (name)
max_nonopt_args++;
if (value)
@@ -345,14 +343,13 @@ static void parse_args(int argc, char **argv,
}
}
- if (object && optind < argc)
- *object = argv[optind++];
- if (name && optind < argc)
+ *object = argv[optind++];
+ if (optind < argc)
*name = argv[optind++];
- if (value && optind < argc)
+ if (optind < argc)
*value = argv[optind++];
- if (!*types && object && *object) {
+ if (!*types) {
ret = autodetect_object_types(*object, types);
if (ret < 0) {
error("failed to detect object type: %s",
@@ -388,10 +385,6 @@ static int cmd_property_get(int argc, char **argv)
parse_args(argc, argv, cmd_property_get_usage, &types, &object, &name,
NULL, 1);
- if (!object) {
- error("invalid arguments");
- usage(cmd_property_get_usage);
- }
if (name)
ret = setget_prop(types, object, name, NULL);
@@ -419,10 +412,6 @@ static int cmd_property_set(int argc, char **argv)
parse_args(argc, argv, cmd_property_set_usage, &types,
&object, &name, &value, 3);
- if (!object || !name || !value) {
- error("invalid arguments");
- usage(cmd_property_set_usage);
- }
ret = setget_prop(types, object, name, value);
@@ -445,10 +434,6 @@ static int cmd_property_list(int argc, char **argv)
parse_args(argc, argv, cmd_property_list_usage,
&types, &object, NULL, NULL, 1);
- if (!object) {
- error("invalid arguments");
- usage(cmd_property_list_usage);
- }
ret = dump_props(types, object, 1);
diff --git a/cmds-receive.c b/cmds-receive.c
index cbb1642..f4a3a4f 100644
--- a/cmds-receive.c
+++ b/cmds-receive.c
@@ -644,7 +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;
- error("cannont open %s: %s", path, strerror(-ret));
+ error("cannot open %s: %s", path, strerror(-ret));
goto out;
}
strncpy_null(r->write_path, path);
diff --git a/cmds-scrub.c b/cmds-scrub.c
index de7005c..4a1d475 100644
--- a/cmds-scrub.c
+++ b/cmds-scrub.c
@@ -430,7 +430,7 @@ static int scrub_rename_file(const char *fn_base, const char *fn_local,
/*
* returns 0 if the key did not match (nothing was read)
* 1 if the key did match (success)
- * -1 if the key did match and an error occured
+ * -1 if the key did match and an error occurred
*/
static int scrub_kvread(int *i, int len, int avail, const char *buf,
const char *key, u64 *dest)
diff --git a/cmds-send.c b/cmds-send.c
index 4063475..74d0128 100644
--- a/cmds-send.c
+++ b/cmds-send.c
@@ -44,7 +44,11 @@
#include "send.h"
#include "send-utils.h"
-static int g_verbose = 0;
+/*
+ * Default is 1 for historical reasons, changing may break scripts that expect
+ * the 'At subvol' message.
+ */
+static int g_verbose = 1;
struct btrfs_send {
int send_fd;
@@ -301,10 +305,10 @@ static int do_send(struct btrfs_send *send, u64 parent_root_id,
"Try upgrading your kernel or don't use -e.\n");
goto out;
}
- if (g_verbose > 0)
+ if (g_verbose > 1)
fprintf(stderr, "BTRFS_IOC_SEND returned %d\n", ret);
- if (g_verbose > 0)
+ if (g_verbose > 1)
fprintf(stderr, "joining genl thread\n");
close(pipefd[1]);
@@ -429,9 +433,11 @@ int cmd_send(int argc, char **argv)
while (1) {
enum { GETOPT_VAL_SEND_NO_DATA = 256 };
static const struct option long_options[] = {
+ { "verbose", no_argument, NULL, 'v' },
+ { "quiet", no_argument, NULL, 'q' },
{ "no-data", no_argument, NULL, GETOPT_VAL_SEND_NO_DATA }
};
- int c = getopt_long(argc, argv, "vec:f:i:p:", long_options, NULL);
+ int c = getopt_long(argc, argv, "vqec:f:i:p:", long_options, NULL);
if (c < 0)
break;
@@ -440,6 +446,9 @@ int cmd_send(int argc, char **argv)
case 'v':
g_verbose++;
break;
+ case 'q':
+ g_verbose = 0;
+ break;
case 'e':
new_end_cmd_semantic = 1;
break;
@@ -617,13 +626,14 @@ int cmd_send(int argc, char **argv)
goto out;
if (!ret) {
ret = -EINVAL;
- error("subvolum %s is not read-only", subvol);
+ error("subvolume %s is not read-only", subvol);
goto out;
}
}
- if (send_flags & BTRFS_SEND_FLAG_NO_FILE_DATA)
- printf("Mode NO_FILE_DATA enabled\n");
+ if ((send_flags & BTRFS_SEND_FLAG_NO_FILE_DATA) && g_verbose > 1)
+ if (g_verbose > 1)
+ fprintf(stderr, "Mode NO_FILE_DATA enabled\n");
for (i = optind; i < argc; i++) {
int is_first_subvol;
@@ -632,7 +642,8 @@ int cmd_send(int argc, char **argv)
free(subvol);
subvol = argv[i];
- fprintf(stderr, "At subvol %s\n", subvol);
+ if (g_verbose > 0)
+ fprintf(stderr, "At subvol %s\n", subvol);
subvol = realpath(subvol, NULL);
if (!subvol) {
@@ -713,8 +724,6 @@ const char * const cmd_send_usage[] = {
"which case 'btrfs send' will determine a suitable parent among the",
"clone sources itself.",
"\n",
- "-v Enable verbose debug output. Each occurrence of",
- " this option increases the verbose level more.",
"-e If sending multiple subvols at once, use the new",
" format and omit the end-cmd between the subvols.",
"-p <parent> Send an incremental stream from <parent> to",
@@ -728,5 +737,8 @@ const char * const cmd_send_usage[] = {
" does not contain any file data and thus cannot be used",
" to transfer changes. This mode is faster and useful to",
" show the differences in metadata.",
+ "-v|--verbose enable verbose output to stderr, each occurrence of",
+ " this option increases verbosity",
+ "-q|--quiet suppress all messages, except errors",
NULL
};
diff --git a/cmds-subvolume.c b/cmds-subvolume.c
index 2319684..e3d2cbc 100644
--- a/cmds-subvolume.c
+++ b/cmds-subvolume.c
@@ -1142,7 +1142,8 @@ static int enumerate_dead_subvols(int fd, u64 **ids)
sh = (struct btrfs_ioctl_search_header*)(args.buf + off);
off += sizeof(*sh);
- if (sh->type == BTRFS_ORPHAN_ITEM_KEY) {
+ if (btrfs_search_header_type(sh)
+ == BTRFS_ORPHAN_ITEM_KEY) {
if (idx >= count) {
u64 *newids;
@@ -1153,14 +1154,14 @@ static int enumerate_dead_subvols(int fd, u64 **ids)
return -ENOMEM;
*ids = newids;
}
- (*ids)[idx] = sh->offset;
+ (*ids)[idx] = btrfs_search_header_offset(sh);
idx++;
}
- off += sh->len;
+ off += btrfs_search_header_len(sh);
- sk->min_objectid = sh->objectid;
- sk->min_type = sh->type;
- sk->min_offset = sh->offset;
+ sk->min_objectid = btrfs_search_header_objectid(sh);
+ sk->min_type = btrfs_search_header_type(sh);
+ sk->min_offset = btrfs_search_header_offset(sh);
}
if (sk->min_offset < (u64)-1)
sk->min_offset++;
@@ -1194,10 +1195,9 @@ static int cmd_subvol_sync(int argc, char **argv)
switch (c) {
case 's':
- sleep_interval = atoi(argv[optind]);
+ sleep_interval = atoi(optarg);
if (sleep_interval < 1) {
- error("invalid sleep interval %s",
- argv[optind]);
+ error("invalid sleep interval %s", optarg);
ret = 1;
goto out;
}
diff --git a/commands.h b/commands.h
index 2da093b..94229c1 100644
--- a/commands.h
+++ b/commands.h
@@ -125,10 +125,4 @@ int cmd_dump_super(int argc, char **argv);
int cmd_debug_tree(int argc, char **argv);
int cmd_rescue(int argc, char **argv);
-/* subvolume exported functions */
-int test_issubvolume(const char *path);
-
-/* send.c */
-char *get_subvol_name(char *mnt, char *full_path);
-
#endif
diff --git a/config.h.in b/config.h.in
index d938e81..2083844 100644
--- a/config.h.in
+++ b/config.h.in
@@ -21,6 +21,9 @@
/* Define to 1 if you have the <memory.h> header file. */
#undef HAVE_MEMORY_H
+/* E2fsprogs does not support BIGALLOC */
+#undef HAVE_OLD_E2FSPROGS
+
/* Define to 1 if you have the `openat' function. */
#undef HAVE_OPENAT
diff --git a/configure b/configure
index 7d51d42..ccf6392 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.5.2.
+# Generated by GNU Autoconf 2.69 for btrfs-progs v4.6.1.
#
# 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.5.2'
-PACKAGE_STRING='btrfs-progs v4.5.2'
+PACKAGE_VERSION='v4.6.1'
+PACKAGE_STRING='btrfs-progs v4.6.1'
PACKAGE_BUGREPORT='linux-btrfs@vger.kernel.org'
PACKAGE_URL='http://btrfs.wiki.kernel.org'
@@ -631,6 +631,7 @@ LIBBTRFS_MAJOR
LZO2_CFLAGS
LZO2_LIBS_STATIC
LZO2_LIBS
+UDEVDIR
ZLIB_LIBS_STATIC
ZLIB_LIBS
ZLIB_CFLAGS
@@ -1287,7 +1288,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.5.2 to adapt to many kinds of systems.
+\`configure' configures btrfs-progs v4.6.1 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1352,7 +1353,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of btrfs-progs v4.5.2:";;
+ short | recursive ) echo "Configuration of btrfs-progs v4.6.1:";;
esac
cat <<\_ACEOF
@@ -1461,7 +1462,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
-btrfs-progs configure v4.5.2
+btrfs-progs configure v4.6.1
generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc.
@@ -1830,7 +1831,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.5.2, which was
+It was created by btrfs-progs $as_me v4.6.1, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
@@ -5369,12 +5370,12 @@ if test -n "$EXT2FS_CFLAGS"; then
pkg_cv_EXT2FS_CFLAGS="$EXT2FS_CFLAGS"
elif test -n "$PKG_CONFIG"; then
if test -n "$PKG_CONFIG" && \
- { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"ext2fs >= 1.41\""; } >&5
- ($PKG_CONFIG --exists --print-errors "ext2fs >= 1.41") 2>&5
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"ext2fs >= 1.42\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "ext2fs >= 1.42") 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
- pkg_cv_EXT2FS_CFLAGS=`$PKG_CONFIG --cflags "ext2fs >= 1.41" 2>/dev/null`
+ pkg_cv_EXT2FS_CFLAGS=`$PKG_CONFIG --cflags "ext2fs >= 1.42" 2>/dev/null`
test "x$?" != "x0" && pkg_failed=yes
else
pkg_failed=yes
@@ -5386,12 +5387,12 @@ if test -n "$EXT2FS_LIBS"; then
pkg_cv_EXT2FS_LIBS="$EXT2FS_LIBS"
elif test -n "$PKG_CONFIG"; then
if test -n "$PKG_CONFIG" && \
- { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"ext2fs >= 1.41\""; } >&5
- ($PKG_CONFIG --exists --print-errors "ext2fs >= 1.41") 2>&5
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"ext2fs >= 1.42\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "ext2fs >= 1.42") 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
- pkg_cv_EXT2FS_LIBS=`$PKG_CONFIG --libs "ext2fs >= 1.41" 2>/dev/null`
+ pkg_cv_EXT2FS_LIBS=`$PKG_CONFIG --libs "ext2fs >= 1.42" 2>/dev/null`
test "x$?" != "x0" && pkg_failed=yes
else
pkg_failed=yes
@@ -5412,14 +5413,73 @@ else
_pkg_short_errors_supported=no
fi
if test $_pkg_short_errors_supported = yes; then
- EXT2FS_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "ext2fs >= 1.41" 2>&1`
+ EXT2FS_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "ext2fs >= 1.42" 2>&1`
else
- EXT2FS_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "ext2fs >= 1.41" 2>&1`
+ EXT2FS_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "ext2fs >= 1.42" 2>&1`
fi
# Put the nasty error message in config.log where it belongs
echo "$EXT2FS_PKG_ERRORS" >&5
- as_fn_error $? "Package requirements (ext2fs >= 1.41) were not met:
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for EXT2FS" >&5
+$as_echo_n "checking for EXT2FS... " >&6; }
+
+if test -n "$EXT2FS_CFLAGS"; then
+ pkg_cv_EXT2FS_CFLAGS="$EXT2FS_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"ext2fs\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "ext2fs") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_EXT2FS_CFLAGS=`$PKG_CONFIG --cflags "ext2fs" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+if test -n "$EXT2FS_LIBS"; then
+ pkg_cv_EXT2FS_LIBS="$EXT2FS_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"ext2fs\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "ext2fs") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_EXT2FS_LIBS=`$PKG_CONFIG --libs "ext2fs" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi
+ if test $_pkg_short_errors_supported = yes; then
+ EXT2FS_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "ext2fs" 2>&1`
+ else
+ EXT2FS_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "ext2fs" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$EXT2FS_PKG_ERRORS" >&5
+
+ as_fn_error $? "Package requirements (ext2fs) were not met:
$EXT2FS_PKG_ERRORS
@@ -5450,6 +5510,113 @@ else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
+$as_echo "#define HAVE_OLD_E2FSPROGS 1" >>confdefs.h
+
+
+fi
+elif test $pkg_failed = untried; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for EXT2FS" >&5
+$as_echo_n "checking for EXT2FS... " >&6; }
+
+if test -n "$EXT2FS_CFLAGS"; then
+ pkg_cv_EXT2FS_CFLAGS="$EXT2FS_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"ext2fs\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "ext2fs") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_EXT2FS_CFLAGS=`$PKG_CONFIG --cflags "ext2fs" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+if test -n "$EXT2FS_LIBS"; then
+ pkg_cv_EXT2FS_LIBS="$EXT2FS_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"ext2fs\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "ext2fs") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_EXT2FS_LIBS=`$PKG_CONFIG --libs "ext2fs" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi
+ if test $_pkg_short_errors_supported = yes; then
+ EXT2FS_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "ext2fs" 2>&1`
+ else
+ EXT2FS_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "ext2fs" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$EXT2FS_PKG_ERRORS" >&5
+
+ as_fn_error $? "Package requirements (ext2fs) were not met:
+
+$EXT2FS_PKG_ERRORS
+
+Consider adjusting the PKG_CONFIG_PATH environment variable if you
+installed software in a non-standard prefix.
+
+Alternatively, you may set the environment variables EXT2FS_CFLAGS
+and EXT2FS_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details." "$LINENO" 5
+elif test $pkg_failed = untried; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it
+is in your PATH or set the PKG_CONFIG environment variable to the full
+path to pkg-config.
+
+Alternatively, you may set the environment variables EXT2FS_CFLAGS
+and EXT2FS_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details.
+
+To get pkg-config, see <http://pkg-config.freedesktop.org/>.
+See \`config.log' for more details" "$LINENO" 5; }
+else
+ EXT2FS_CFLAGS=$pkg_cv_EXT2FS_CFLAGS
+ EXT2FS_LIBS=$pkg_cv_EXT2FS_LIBS
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+$as_echo "#define HAVE_OLD_E2FSPROGS 1" >>confdefs.h
+
+
+fi
+else
+ EXT2FS_CFLAGS=$pkg_cv_EXT2FS_CFLAGS
+ EXT2FS_LIBS=$pkg_cv_EXT2FS_LIBS
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
fi
pkg_failed=no
@@ -5545,6 +5712,43 @@ fi
fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for FIEMAP_EXTENT_SHARED defined in linux/fiemap.h" >&5
+$as_echo_n "checking for FIEMAP_EXTENT_SHARED defined in linux/fiemap.h... " >&6; }
+if ${ac_cv_defined_FIEMAP_EXTENT_SHARED_linux_fiemap_h+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <linux/fiemap.h>
+int
+main ()
+{
+
+ #ifdef FIEMAP_EXTENT_SHARED
+ int ok;
+ #else
+ choke me
+ #endif
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_defined_FIEMAP_EXTENT_SHARED_linux_fiemap_h=yes
+else
+ ac_cv_defined_FIEMAP_EXTENT_SHARED_linux_fiemap_h=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_defined_FIEMAP_EXTENT_SHARED_linux_fiemap_h" >&5
+$as_echo "$ac_cv_defined_FIEMAP_EXTENT_SHARED_linux_fiemap_h" >&6; }
+if test $ac_cv_defined_FIEMAP_EXTENT_SHARED_linux_fiemap_h != "no"; then :
+
+else
+ as_fn_error $? "no definition of FIEMAP_EXTENT_SHARED found" "$LINENO" 5
+fi
+
pkg_failed=no
@@ -5858,6 +6062,9 @@ fi
fi
+UDEVDIR="$(pkg-config udev --variable=udevdir)"
+
+
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for lzo_version in -llzo2" >&5
$as_echo_n "checking for lzo_version in -llzo2... " >&6; }
if ${ac_cv_lib_lzo2_lzo_version+:} false; then :
@@ -6427,7 +6634,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.5.2, which was
+This file was extended by btrfs-progs $as_me v4.6.1, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -6490,7 +6697,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.5.2
+btrfs-progs config.status v4.6.1
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
diff --git a/configure.ac b/configure.ac
index 797eb79..901d434 100644
--- a/configure.ac
+++ b/configure.ac
@@ -105,10 +105,16 @@ AS_IF([test "x$enable_convert" = xyes], [DISABLE_BTRFSCONVERT=0], [DISABLE_BTRFS
AC_SUBST([DISABLE_BTRFSCONVERT])
if test "x$enable_convert" = xyes; then
- PKG_CHECK_MODULES(EXT2FS, [ext2fs >= 1.41])
+ PKG_CHECK_MODULES(EXT2FS, [ext2fs >= 1.42],,
+ [PKG_CHECK_MODULES(EXT2FS, [ext2fs],
+ [AC_DEFINE([HAVE_OLD_E2FSPROGS], [1],
+ [E2fsprogs does not support BIGALLOC])]
+ )])
PKG_CHECK_MODULES(COM_ERR, [com_err])
fi
+AX_CHECK_DEFINE([linux/fiemap.h], [FIEMAP_EXTENT_SHARED], [],
+ [AC_MSG_ERROR([no definition of FIEMAP_EXTENT_SHARED found])])
dnl Define <NAME>_LIBS= and <NAME>_CFLAGS= by pkg-config
dnl
@@ -124,6 +130,9 @@ PKG_STATIC(UUID_LIBS_STATIC, [uuid])
PKG_CHECK_MODULES(ZLIB, [zlib])
PKG_STATIC(ZLIB_LIBS_STATIC, [zlib])
+UDEVDIR="$(pkg-config udev --variable=udevdir)"
+AC_SUBST(UDEVDIR)
+
dnl lzo library does not provide pkg-config, let use classic way
AC_CHECK_LIB([lzo2], [lzo_version], [
LZO2_LIBS="-llzo2"
diff --git a/ctree.c b/ctree.c
index c60f609..a98ad18 100644
--- a/ctree.c
+++ b/ctree.c
@@ -142,7 +142,7 @@ static int btrfs_block_can_be_shared(struct btrfs_root *root,
struct extent_buffer *buf)
{
/*
- * Tree blocks not in refernece counted trees and tree roots
+ * Tree blocks not in reference counted trees and tree roots
* are never shared. If a block was allocated after the last
* snapshot and the block was not allocated by tree relocation,
* we know the block is not shared.
@@ -2880,6 +2880,7 @@ int btrfs_previous_item(struct btrfs_root *root,
{
struct btrfs_key found_key;
struct extent_buffer *leaf;
+ u32 nritems;
int ret;
while(1) {
@@ -2891,9 +2892,20 @@ int btrfs_previous_item(struct btrfs_root *root,
path->slots[0]--;
}
leaf = path->nodes[0];
+ nritems = btrfs_header_nritems(leaf);
+ if (nritems == 0)
+ return 1;
+ if (path->slots[0] == nritems)
+ path->slots[0]--;
+
btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
+ if (found_key.objectid < min_objectid)
+ break;
if (found_key.type == type)
return 0;
+ if (found_key.objectid == min_objectid &&
+ found_key.type < type)
+ break;
}
return 1;
}
@@ -2939,3 +2951,27 @@ int btrfs_previous_extent_item(struct btrfs_root *root,
}
return 1;
}
+
+/*
+ * Search in extent tree to found next meta/data extent
+ * Caller needs to check for no-hole or skinny metadata features.
+ */
+int btrfs_next_extent_item(struct btrfs_root *root,
+ struct btrfs_path *path, u64 max_objectid)
+{
+ struct btrfs_key found_key;
+ int ret;
+
+ while (1) {
+ ret = btrfs_next_item(root, path);
+ if (ret)
+ return ret;
+ btrfs_item_key_to_cpu(path->nodes[0], &found_key,
+ path->slots[0]);
+ if (found_key.objectid > max_objectid)
+ return 1;
+ if (found_key.type == BTRFS_EXTENT_ITEM_KEY ||
+ found_key.type == BTRFS_METADATA_ITEM_KEY)
+ return 0;
+ }
+}
diff --git a/ctree.h b/ctree.h
index 2db5c87..9e3626f 100644
--- a/ctree.h
+++ b/ctree.h
@@ -173,7 +173,7 @@ static int btrfs_csum_sizes[] = { 4 };
/*
* the key defines the order in the tree, and so it also defines (optimal)
- * block layout. objectid corresonds to the inode number. The flags
+ * block layout. objectid corresponds to the inode number. The flags
* tells us things about the object, and is a kind of stream selector.
* so for a given inode, keys with flags of 1 might refer to the inode
* data, flags of 2 may point to file data in the btree and flags == 3
@@ -229,7 +229,7 @@ struct btrfs_dev_item {
/*
* starting byte of this partition on the device,
- * to allowr for stripe alignment in the future
+ * to allow for stripe alignment in the future
*/
__le64 start_offset;
@@ -578,6 +578,7 @@ struct btrfs_extent_item_v0 {
#define BTRFS_MAX_EXTENT_ITEM_SIZE(r) ((BTRFS_LEAF_DATA_SIZE(r) >> 4) - \
sizeof(struct btrfs_item))
+#define BTRFS_MAX_EXTENT_SIZE (128 * 1024 * 1024)
#define BTRFS_EXTENT_FLAG_DATA (1ULL << 0)
#define BTRFS_EXTENT_FLAG_TREE_BLOCK (1ULL << 1)
@@ -746,7 +747,7 @@ struct btrfs_root_item {
/*
* This generation number is used to test if the new fields are valid
- * and up to date while reading the root item. Everytime the root item
+ * and up to date while reading the root item. Every time the root item
* is written out, the "generation" field is copied into this field. If
* anyone ever mounted the fs with an older kernel, we will have
* mismatching generation values here and thus must invalidate the
@@ -959,13 +960,6 @@ struct btrfs_block_group_cache {
int ro;
};
-struct btrfs_extent_ops {
- int (*alloc_extent)(struct btrfs_root *root, u64 num_bytes,
- u64 hint_byte, struct btrfs_key *ins, int metadata);
- int (*free_extent)(struct btrfs_root *root, u64 bytenr,
- u64 num_bytes);
-};
-
struct btrfs_device;
struct btrfs_fs_devices;
struct btrfs_fs_info {
@@ -1016,7 +1010,6 @@ struct btrfs_fs_info {
u64 super_bytenr;
u64 total_pinned;
- struct btrfs_extent_ops *extent_ops;
struct list_head dirty_cowonly_roots;
struct list_head recow_ebs;
@@ -1031,6 +1024,8 @@ struct btrfs_fs_info {
unsigned int suppress_check_block_errors:1;
unsigned int ignore_fsid_mismatch:1;
unsigned int ignore_chunk_tree_error:1;
+ unsigned int avoid_meta_chunk_alloc:1;
+ unsigned int avoid_sys_chunk_alloc:1;
int (*free_extent_hook)(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
@@ -2201,6 +2196,32 @@ static inline u32 btrfs_file_extent_inline_item_len(struct extent_buffer *eb,
return btrfs_item_size(eb, e) - offset;
}
+/* struct btrfs_ioctl_search_header */
+static inline u64 btrfs_search_header_transid(struct btrfs_ioctl_search_header *sh)
+{
+ return get_unaligned_64(&sh->transid);
+}
+
+static inline u64 btrfs_search_header_objectid(struct btrfs_ioctl_search_header *sh)
+{
+ return get_unaligned_64(&sh->objectid);
+}
+
+static inline u64 btrfs_search_header_offset(struct btrfs_ioctl_search_header *sh)
+{
+ return get_unaligned_64(&sh->offset);
+}
+
+static inline u32 btrfs_search_header_type(struct btrfs_ioctl_search_header *sh)
+{
+ return get_unaligned_32(&sh->type);
+}
+
+static inline u32 btrfs_search_header_len(struct btrfs_ioctl_search_header *sh)
+{
+ return get_unaligned_32(&sh->len);
+}
+
/* this returns the number of file bytes represented by the inline item.
* If an item is compressed, this is the uncompressed size
*/
@@ -2362,6 +2383,8 @@ int btrfs_previous_item(struct btrfs_root *root,
int type);
int btrfs_previous_extent_item(struct btrfs_root *root,
struct btrfs_path *path, u64 min_objectid);
+int btrfs_next_extent_item(struct btrfs_root *root,
+ struct btrfs_path *path, u64 max_objectid);
int btrfs_cow_block(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct extent_buffer *buf,
struct extent_buffer *parent, int parent_slot,
diff --git a/debian/changelog b/debian/changelog
index 49e70fd..7ff6ff4 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,6 +1,7 @@
-btrfs-progs (4.5.2-2) UNRELEASED; urgency=high
+btrfs-progs (4.6.1-1) UNRELEASED; urgency=high
[ Dimitri John Ledkov ]
+ * New upstream release
* Update debian/copyright as package is mostly GPL-2 only, not GPL-2+
(Closes: #824896)
* Provide upstream changelog, thanks to Nicholas D Steeves (Closes:
diff --git a/dir-item.c b/dir-item.c
index bc59d17..c2ae953 100644
--- a/dir-item.c
+++ b/dir-item.c
@@ -86,7 +86,7 @@ int btrfs_insert_xattr_item(struct btrfs_trans_handle *trans,
name, name_len);
/*
* FIXME: at some point we should handle xattr's that are larger than
- * what we can fit in our leaf. We set location to NULL b/c we arent
+ * what we can fit in our leaf. We set location to NULL b/c we aren't
* pointing at anything else, that will change if we store the xattr
* data in a separate inode.
*/
diff --git a/disk-io.c b/disk-io.c
index f1d4697..fbce506 100644
--- a/disk-io.c
+++ b/disk-io.c
@@ -67,6 +67,11 @@ static int check_tree_block(struct btrfs_fs_info *fs_info,
nodesize))
return BTRFS_BAD_NRITEMS;
+ /* Only leaf can be empty */
+ if (btrfs_header_nritems(buf) == 0 &&
+ btrfs_header_level(buf) != 0)
+ return BTRFS_BAD_NRITEMS;
+
fs_devices = fs_info->fs_devices;
while (fs_devices) {
if (fs_info->ignore_fsid_mismatch ||
@@ -1262,7 +1267,7 @@ static struct btrfs_fs_info *__open_ctree_fd(int fp, const char *path,
goto out;
disk_super = fs_info->super_copy;
- if (!(flags & OPEN_CTREE_RECOVER_SUPER))
+ if (flags & OPEN_CTREE_RECOVER_SUPER)
ret = btrfs_read_dev_super(fs_devices->latest_bdev,
disk_super, sb_bytenr, 1);
else
@@ -1323,7 +1328,7 @@ struct btrfs_fs_info *open_ctree_fs_info(const char *filename,
int fp;
int ret;
struct btrfs_fs_info *info;
- int oflags = O_CREAT | O_RDWR;
+ int oflags = O_RDWR;
struct stat st;
ret = stat(filename, &st);
@@ -1339,7 +1344,7 @@ struct btrfs_fs_info *open_ctree_fs_info(const char *filename,
if (!(flags & OPEN_CTREE_WRITES))
oflags = O_RDONLY;
- fp = open(filename, oflags, 0600);
+ fp = open(filename, oflags);
if (fp < 0) {
error("cannot open '%s': %s", filename, strerror(errno));
return NULL;
@@ -1395,14 +1400,13 @@ static int check_super(struct btrfs_super_block *sb)
int csum_size;
if (btrfs_super_magic(sb) != BTRFS_MAGIC) {
- fprintf(stderr, "ERROR: superblock magic doesn't match\n");
+ error("superblock magic doesn't match");
return -EIO;
}
csum_type = btrfs_super_csum_type(sb);
if (csum_type >= ARRAY_SIZE(btrfs_csum_sizes)) {
- fprintf(stderr, "ERROR: unsupported checksum algorithm %u\n",
- csum_type);
+ error("unsupported checksum algorithm %u\n", csum_type);
return -EIO;
}
csum_size = btrfs_csum_sizes[csum_type];
@@ -1413,59 +1417,69 @@ static int check_super(struct btrfs_super_block *sb)
btrfs_csum_final(crc, result);
if (memcmp(result, sb->csum, csum_size)) {
- fprintf(stderr, "ERROR: superblock checksum mismatch\n");
+ error("superblock checksum mismatch");
return -EIO;
}
if (btrfs_super_root_level(sb) >= BTRFS_MAX_LEVEL) {
- fprintf(stderr, "ERROR: tree_root level too big: %d >= %d\n",
+ error("tree_root level too big: %d >= %d",
btrfs_super_root_level(sb), BTRFS_MAX_LEVEL);
- return -EIO;
+ goto error_out;
}
if (btrfs_super_chunk_root_level(sb) >= BTRFS_MAX_LEVEL) {
- fprintf(stderr, "ERROR: chunk_root level too big: %d >= %d\n",
+ error("chunk_root level too big: %d >= %d",
btrfs_super_chunk_root_level(sb), BTRFS_MAX_LEVEL);
- return -EIO;
+ goto error_out;
}
if (btrfs_super_log_root_level(sb) >= BTRFS_MAX_LEVEL) {
- fprintf(stderr, "ERROR: log_root level too big: %d >= %d\n",
+ error("log_root level too big: %d >= %d",
btrfs_super_log_root_level(sb), BTRFS_MAX_LEVEL);
- return -EIO;
+ goto error_out;
}
if (!IS_ALIGNED(btrfs_super_root(sb), 4096)) {
- fprintf(stderr, "ERROR: tree_root block unaligned: %llu\n",
- btrfs_super_root(sb));
- return -EIO;
+ error("tree_root block unaligned: %llu", btrfs_super_root(sb));
+ goto error_out;
}
if (!IS_ALIGNED(btrfs_super_chunk_root(sb), 4096)) {
- fprintf(stderr, "ERROR: chunk_root block unaligned: %llu\n",
+ error("chunk_root block unaligned: %llu",
btrfs_super_chunk_root(sb));
- return -EIO;
+ goto error_out;
}
if (!IS_ALIGNED(btrfs_super_log_root(sb), 4096)) {
- fprintf(stderr, "ERROR: log_root block unaligned: %llu\n",
+ error("log_root block unaligned: %llu",
btrfs_super_log_root(sb));
- return -EIO;
+ goto error_out;
}
if (btrfs_super_nodesize(sb) < 4096) {
- fprintf(stderr, "ERROR: nodesize too small: %u < 4096\n",
+ error("nodesize too small: %u < 4096",
btrfs_super_nodesize(sb));
- return -EIO;
+ goto error_out;
}
if (!IS_ALIGNED(btrfs_super_nodesize(sb), 4096)) {
- fprintf(stderr, "ERROR: nodesize unaligned: %u\n",
- btrfs_super_nodesize(sb));
- return -EIO;
+ error("nodesize unaligned: %u", btrfs_super_nodesize(sb));
+ goto error_out;
}
if (btrfs_super_sectorsize(sb) < 4096) {
- fprintf(stderr, "ERROR: sectorsize too small: %u < 4096\n",
+ error("sectorsize too small: %u < 4096",
btrfs_super_sectorsize(sb));
- return -EIO;
+ goto error_out;
}
if (!IS_ALIGNED(btrfs_super_sectorsize(sb), 4096)) {
- fprintf(stderr, "ERROR: sectorsize unaligned: %u\n",
- btrfs_super_sectorsize(sb));
- return -EIO;
+ error("sectorsize unaligned: %u", btrfs_super_sectorsize(sb));
+ goto error_out;
+ }
+ if (btrfs_super_total_bytes(sb) == 0) {
+ error("invalid total_bytes 0");
+ goto error_out;
+ }
+ if (btrfs_super_bytes_used(sb) < 6 * btrfs_super_nodesize(sb)) {
+ error("invalid bytes_used %llu", btrfs_super_bytes_used(sb));
+ goto error_out;
+ }
+ if ((btrfs_super_stripesize(sb) != 4096)
+ && (btrfs_super_stripesize(sb) != btrfs_super_sectorsize(sb))) {
+ error("invalid stripesize %u", btrfs_super_stripesize(sb));
+ goto error_out;
}
if (memcmp(sb->fsid, sb->dev_item.fsid, BTRFS_UUID_SIZE) != 0) {
@@ -1474,23 +1488,22 @@ static int check_super(struct btrfs_super_block *sb)
uuid_unparse(sb->fsid, fsid);
uuid_unparse(sb->dev_item.fsid, dev_fsid);
- printk(KERN_ERR
- "ERROR: dev_item UUID does not match fsid: %s != %s\n",
+ error("dev_item UUID does not match fsid: %s != %s",
dev_fsid, fsid);
- return -EIO;
+ goto error_out;
}
/*
* Hint to catch really bogus numbers, bitflips or so
*/
if (btrfs_super_num_devices(sb) > (1UL << 31)) {
- fprintf(stderr, "WARNING: suspicious number of devices: %llu\n",
+ warning("suspicious number of devices: %llu",
btrfs_super_num_devices(sb));
}
if (btrfs_super_num_devices(sb) == 0) {
- fprintf(stderr, "ERROR: number of devices is 0\n");
- return -EIO;
+ error("number of devices is 0");
+ goto error_out;
}
/*
@@ -1498,21 +1511,25 @@ static int check_super(struct btrfs_super_block *sb)
* and one chunk
*/
if (btrfs_super_sys_array_size(sb) > BTRFS_SYSTEM_CHUNK_ARRAY_SIZE) {
- fprintf(stderr, "BTRFS: system chunk array too big %u > %u\n",
- btrfs_super_sys_array_size(sb),
- BTRFS_SYSTEM_CHUNK_ARRAY_SIZE);
- return -EIO;
+ error("system chunk array too big %u > %u",
+ btrfs_super_sys_array_size(sb),
+ BTRFS_SYSTEM_CHUNK_ARRAY_SIZE);
+ goto error_out;
}
if (btrfs_super_sys_array_size(sb) < sizeof(struct btrfs_disk_key)
+ sizeof(struct btrfs_chunk)) {
- fprintf(stderr, "BTRFS: system chunk array too small %u < %lu\n",
- btrfs_super_sys_array_size(sb),
- sizeof(struct btrfs_disk_key) +
- sizeof(struct btrfs_chunk));
- return -EIO;
+ error("system chunk array too small %u < %lu",
+ btrfs_super_sys_array_size(sb),
+ sizeof(struct btrfs_disk_key) +
+ sizeof(struct btrfs_chunk));
+ goto error_out;
}
return 0;
+
+error_out:
+ error("superblock checksum matches but it has invalid members");
+ return -EIO;
}
int btrfs_read_dev_super(int fd, struct btrfs_super_block *sb, u64 sb_bytenr,
diff --git a/disk-io.h b/disk-io.h
index b97b90b..d860ff2 100644
--- a/disk-io.h
+++ b/disk-io.h
@@ -48,8 +48,8 @@ enum btrfs_open_ctree_flags {
OPEN_CTREE_SUPPRESS_CHECK_BLOCK_ERRORS +
__OPEN_CTREE_RETURN_CHUNK_ROOT,
/*
- * TODO: cleanup: Split the open_ctree_flags into more indepent
- * tree bits.
+ * TODO: cleanup: Split the open_ctree_flags into more independent
+ * Tree bits.
* Like split PARTIAL into SKIP_CSUM/SKIP_EXTENT
*/
diff --git a/extent-cache.h b/extent-cache.h
index f031fbf..82db7fa 100644
--- a/extent-cache.h
+++ b/extent-cache.h
@@ -53,7 +53,7 @@ struct cache_extent *next_cache_extent(struct cache_extent *pe);
struct cache_extent *search_cache_extent(struct cache_tree *tree, u64 start);
/*
- * Find a cahce_extent which restrictly covers start.
+ * Find a cache_extent which restrictly covers start.
*
* If not found, return NULL.
*/
diff --git a/extent-tree.c b/extent-tree.c
index 4a41717..5ca53fa 100644
--- a/extent-tree.c
+++ b/extent-tree.c
@@ -1904,6 +1904,16 @@ static int do_chunk_alloc(struct btrfs_trans_handle *trans,
thresh)
return 0;
+ /*
+ * Avoid allocating given chunk type
+ */
+ if (extent_root->fs_info->avoid_meta_chunk_alloc &&
+ (flags & BTRFS_BLOCK_GROUP_METADATA))
+ return 0;
+ if (extent_root->fs_info->avoid_sys_chunk_alloc &&
+ (flags & BTRFS_BLOCK_GROUP_SYSTEM))
+ return 0;
+
ret = btrfs_alloc_chunk(trans, extent_root, &start, &num_bytes,
space_info->flags);
if (ret == -ENOSPC) {
@@ -2171,7 +2181,6 @@ static int __free_extent(struct btrfs_trans_handle *trans,
struct btrfs_key key;
struct btrfs_path *path;
- struct btrfs_extent_ops *ops = root->fs_info->extent_ops;
struct btrfs_root *extent_root = root->fs_info->extent_root;
struct extent_buffer *leaf;
struct btrfs_extent_item *ei;
@@ -2372,14 +2381,6 @@ static int __free_extent(struct btrfs_trans_handle *trans,
}
}
- if (ops && ops->free_extent) {
- ret = ops->free_extent(root, bytenr, num_bytes);
- if (ret > 0) {
- pin = 0;
- mark_free = 0;
- }
- }
-
if (pin) {
ret = pin_down_bytes(trans, root, bytenr, num_bytes,
is_data);
@@ -2652,13 +2653,6 @@ int btrfs_reserve_extent(struct btrfs_trans_handle *trans,
u64 alloc_profile;
struct btrfs_fs_info *info = root->fs_info;
- if (info->extent_ops) {
- struct btrfs_extent_ops *ops = info->extent_ops;
- ret = ops->alloc_extent(root, num_bytes, hint_byte, ins, !data);
- BUG_ON(ret);
- goto found;
- }
-
if (data) {
alloc_profile = info->avail_data_alloc_bits &
info->data_alloc_profile;
@@ -2692,7 +2686,6 @@ int btrfs_reserve_extent(struct btrfs_trans_handle *trans,
trans->alloc_exclude_start,
trans->alloc_exclude_nr, data);
BUG_ON(ret);
-found:
clear_extent_dirty(&root->fs_info->free_space_cache,
ins->objectid, ins->objectid + ins->offset - 1,
GFP_NOFS);
@@ -3909,16 +3902,74 @@ int btrfs_fix_block_accounting(struct btrfs_trans_handle *trans,
return 0;
}
+static void __get_extent_size(struct btrfs_root *root, struct btrfs_path *path,
+ u64 *start, u64 *len)
+{
+ struct btrfs_key key;
+
+ btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
+ BUG_ON(!(key.type == BTRFS_EXTENT_ITEM_KEY ||
+ key.type == BTRFS_METADATA_ITEM_KEY));
+ *start = key.objectid;
+ if (key.type == BTRFS_EXTENT_ITEM_KEY)
+ *len = key.offset;
+ else
+ *len = root->nodesize;
+}
+
/*
- * Record a file extent. Do all the required works, such as inserting
- * file extent item, inserting extent item and backref item into extent
- * tree and updating block accounting.
+ * Find first overlap extent for range [bytenr, bytenr + len)
+ * Return 0 for found and point path to it.
+ * Return >0 for not found.
+ * Return <0 for err
*/
-int btrfs_record_file_extent(struct btrfs_trans_handle *trans,
- struct btrfs_root *root, u64 objectid,
- struct btrfs_inode_item *inode,
- u64 file_pos, u64 disk_bytenr,
- u64 num_bytes)
+int btrfs_search_overlap_extent(struct btrfs_root *root,
+ struct btrfs_path *path, u64 bytenr, u64 len)
+{
+ struct btrfs_key key;
+ u64 cur_start;
+ u64 cur_len;
+ int ret;
+
+ key.objectid = bytenr;
+ key.type = BTRFS_EXTENT_DATA_KEY;
+ key.offset = (u64)-1;
+
+ ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+ if (ret < 0)
+ return ret;
+ BUG_ON(ret == 0);
+
+ ret = btrfs_previous_extent_item(root, path, 0);
+ if (ret < 0)
+ return ret;
+ /* no previous, check next extent */
+ if (ret > 0)
+ goto next;
+ __get_extent_size(root, path, &cur_start, &cur_len);
+ /* Tail overlap */
+ if (cur_start + cur_len > bytenr)
+ return 1;
+
+next:
+ ret = btrfs_next_extent_item(root, path, bytenr + len);
+ if (ret < 0)
+ return ret;
+ /* No next, prev already checked, no overlap */
+ if (ret > 0)
+ return 0;
+ __get_extent_size(root, path, &cur_start, &cur_len);
+ /* head overlap*/
+ if (cur_start < bytenr + len)
+ return 1;
+ return 0;
+}
+
+static int __btrfs_record_file_extent(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root, u64 objectid,
+ struct btrfs_inode_item *inode,
+ u64 file_pos, u64 disk_bytenr,
+ u64 *ret_num_bytes)
{
int ret;
struct btrfs_fs_info *info = root->fs_info;
@@ -3926,10 +3977,19 @@ int btrfs_record_file_extent(struct btrfs_trans_handle *trans,
struct extent_buffer *leaf;
struct btrfs_file_extent_item *fi;
struct btrfs_key ins_key;
- struct btrfs_path path;
+ struct btrfs_path *path;
struct btrfs_extent_item *ei;
u64 nbytes;
+ u64 extent_num_bytes;
+ u64 extent_bytenr;
+ u64 extent_offset;
+ u64 num_bytes = *ret_num_bytes;
+ num_bytes = min_t(u64, num_bytes, BTRFS_MAX_EXTENT_SIZE);
+ /*
+ * All supported file system should not use its 0 extent.
+ * As it's for hole
+ */
if (disk_bytenr == 0) {
ret = btrfs_insert_file_extent(trans, root, objectid,
file_pos, disk_bytenr,
@@ -3937,25 +3997,80 @@ int btrfs_record_file_extent(struct btrfs_trans_handle *trans,
return ret;
}
- btrfs_init_path(&path);
+ path = btrfs_alloc_path();
+ if (!path)
+ return -ENOMEM;
+ /* First to check extent overlap */
+ ret = btrfs_search_overlap_extent(extent_root, path, disk_bytenr,
+ num_bytes);
+ if (ret < 0)
+ goto fail;
+ if (ret > 0) {
+ /* Found overlap */
+ u64 cur_start;
+ u64 cur_len;
+
+ __get_extent_size(extent_root, path, &cur_start, &cur_len);
+ /*
+ * For convert case, this extent should be a subset of
+ * existing one.
+ */
+ BUG_ON(disk_bytenr < cur_start);
+
+ extent_bytenr = cur_start;
+ extent_num_bytes = cur_len;
+ extent_offset = disk_bytenr - extent_bytenr;
+ } else {
+ /* No overlap, create new extent */
+ btrfs_release_path(path);
+ ins_key.objectid = disk_bytenr;
+ ins_key.offset = num_bytes;
+ ins_key.type = BTRFS_EXTENT_ITEM_KEY;
+
+ ret = btrfs_insert_empty_item(trans, extent_root, path,
+ &ins_key, sizeof(*ei));
+ if (ret == 0) {
+ leaf = path->nodes[0];
+ ei = btrfs_item_ptr(leaf, path->slots[0],
+ struct btrfs_extent_item);
+
+ btrfs_set_extent_refs(leaf, ei, 0);
+ btrfs_set_extent_generation(leaf, ei, 0);
+ btrfs_set_extent_flags(leaf, ei,
+ BTRFS_EXTENT_FLAG_DATA);
+ btrfs_mark_buffer_dirty(leaf);
+
+ ret = btrfs_update_block_group(trans, root, disk_bytenr,
+ num_bytes, 1, 0);
+ if (ret)
+ goto fail;
+ } else if (ret != -EEXIST) {
+ goto fail;
+ }
+ btrfs_extent_post_op(trans, extent_root);
+ extent_bytenr = disk_bytenr;
+ extent_num_bytes = num_bytes;
+ extent_offset = 0;
+ }
+ btrfs_release_path(path);
ins_key.objectid = objectid;
ins_key.offset = file_pos;
btrfs_set_key_type(&ins_key, BTRFS_EXTENT_DATA_KEY);
- ret = btrfs_insert_empty_item(trans, root, &path, &ins_key,
+ ret = btrfs_insert_empty_item(trans, root, path, &ins_key,
sizeof(*fi));
if (ret)
goto fail;
- leaf = path.nodes[0];
- fi = btrfs_item_ptr(leaf, path.slots[0],
+ leaf = path->nodes[0];
+ fi = btrfs_item_ptr(leaf, path->slots[0],
struct btrfs_file_extent_item);
btrfs_set_file_extent_generation(leaf, fi, trans->transid);
btrfs_set_file_extent_type(leaf, fi, BTRFS_FILE_EXTENT_REG);
- btrfs_set_file_extent_disk_bytenr(leaf, fi, disk_bytenr);
- btrfs_set_file_extent_disk_num_bytes(leaf, fi, num_bytes);
- btrfs_set_file_extent_offset(leaf, fi, 0);
+ btrfs_set_file_extent_disk_bytenr(leaf, fi, extent_bytenr);
+ btrfs_set_file_extent_disk_num_bytes(leaf, fi, extent_num_bytes);
+ btrfs_set_file_extent_offset(leaf, fi, extent_offset);
btrfs_set_file_extent_num_bytes(leaf, fi, num_bytes);
- btrfs_set_file_extent_ram_bytes(leaf, fi, num_bytes);
+ btrfs_set_file_extent_ram_bytes(leaf, fi, extent_num_bytes);
btrfs_set_file_extent_compression(leaf, fi, 0);
btrfs_set_file_extent_encryption(leaf, fi, 0);
btrfs_set_file_extent_other_encoding(leaf, fi, 0);
@@ -3963,43 +4078,47 @@ int btrfs_record_file_extent(struct btrfs_trans_handle *trans,
nbytes = btrfs_stack_inode_nbytes(inode) + num_bytes;
btrfs_set_stack_inode_nbytes(inode, nbytes);
+ btrfs_release_path(path);
- btrfs_release_path(&path);
-
- ins_key.objectid = disk_bytenr;
- ins_key.offset = num_bytes;
- ins_key.type = BTRFS_EXTENT_ITEM_KEY;
-
- ret = btrfs_insert_empty_item(trans, extent_root, &path,
- &ins_key, sizeof(*ei));
- if (ret == 0) {
- leaf = path.nodes[0];
- ei = btrfs_item_ptr(leaf, path.slots[0],
- struct btrfs_extent_item);
-
- btrfs_set_extent_refs(leaf, ei, 0);
- btrfs_set_extent_generation(leaf, ei, 0);
- btrfs_set_extent_flags(leaf, ei, BTRFS_EXTENT_FLAG_DATA);
-
- btrfs_mark_buffer_dirty(leaf);
-
- ret = btrfs_update_block_group(trans, root, disk_bytenr,
- num_bytes, 1, 0);
- if (ret)
- goto fail;
- } else if (ret != -EEXIST) {
- goto fail;
- }
- btrfs_extent_post_op(trans, extent_root);
-
- ret = btrfs_inc_extent_ref(trans, root, disk_bytenr, num_bytes, 0,
- root->root_key.objectid,
- objectid, file_pos);
+ ret = btrfs_inc_extent_ref(trans, root, extent_bytenr, extent_num_bytes,
+ 0, root->root_key.objectid, objectid,
+ file_pos - extent_offset);
if (ret)
goto fail;
ret = 0;
+ *ret_num_bytes = min(extent_num_bytes - extent_offset, num_bytes);
fail:
- btrfs_release_path(&path);
+ btrfs_free_path(path);
+ return ret;
+}
+
+/*
+ * Record a file extent. Do all the required works, such as inserting
+ * file extent item, inserting extent item and backref item into extent
+ * tree and updating block accounting.
+ */
+int btrfs_record_file_extent(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root, u64 objectid,
+ struct btrfs_inode_item *inode,
+ u64 file_pos, u64 disk_bytenr,
+ u64 num_bytes)
+{
+ u64 cur_disk_bytenr = disk_bytenr;
+ u64 cur_file_pos = file_pos;
+ u64 cur_num_bytes = num_bytes;
+ int ret = 0;
+
+ while (num_bytes > 0) {
+ ret = __btrfs_record_file_extent(trans, root, objectid,
+ inode, cur_file_pos,
+ cur_disk_bytenr,
+ &cur_num_bytes);
+ if (ret < 0)
+ break;
+ cur_disk_bytenr += cur_num_bytes;
+ cur_file_pos += cur_num_bytes;
+ num_bytes -= cur_num_bytes;
+ }
return ret;
}
diff --git a/file.c b/file.c
index 0e9253e..dd0b04b 100644
--- a/file.c
+++ b/file.c
@@ -52,7 +52,7 @@ int btrfs_get_extent(struct btrfs_trans_handle *trans,
if (ret <= 0)
goto out;
if (ret > 0) {
- /* Check preivous file extent */
+ /* Check previous file extent */
ret = btrfs_previous_item(root, path, ino,
BTRFS_EXTENT_DATA_KEY);
if (ret < 0)
@@ -111,7 +111,7 @@ check_next:
not_found = 1;
/*
- * To keep the search hehavior consistent with search_slot(),
+ * To keep the search behavior consistent with search_slot(),
* we need to go back to the prev leaf's nritem slot if
* we are at the first slot of the leaf.
*/
diff --git a/inode.c b/inode.c
index be03a52..ed6d529 100644
--- a/inode.c
+++ b/inode.c
@@ -20,7 +20,7 @@
* Unlike inode.c in kernel, which can use most of the kernel infrastructure
* like inode/dentry things, in user-land, we can only use inode number to
* do directly operation on extent buffer, which may cause extra searching,
- * but should not be a huge problem since progs is less performence sensitive.
+ * but should not be a huge problem since progs is less performance sensitive.
*/
#include <sys/stat.h>
@@ -262,7 +262,7 @@ int btrfs_add_orphan_item(struct btrfs_trans_handle *trans,
* dir_item if any of them exists.
*
* If an inode's nlink is reduced to 0 and 'add_orphan' is true, it will be
- * added to orphan inode and wairing to be deleted by next kernel mount.
+ * added to orphan inode and waiting to be deleted by next kernel mount.
*/
int btrfs_unlink(struct btrfs_trans_handle *trans, struct btrfs_root *root,
u64 ino, u64 parent_ino, u64 index, const char *name,
diff --git a/ioctl.h b/ioctl.h
index 7c80807..5f18bcb 100644
--- a/ioctl.h
+++ b/ioctl.h
@@ -109,7 +109,7 @@ struct btrfs_scrub_progress {
__u64 tree_bytes_scrubbed; /* # of tree bytes scrubbed */
__u64 read_errors; /* # of read errors encountered (EIO) */
__u64 csum_errors; /* # of failed csum checks */
- __u64 verify_errors; /* # of occurences, where the metadata
+ __u64 verify_errors; /* # of occurrences, where the metadata
* of a tree block did not match the
* expected values, like generation or
* logical */
@@ -129,7 +129,7 @@ struct btrfs_scrub_progress {
__u64 last_physical; /* last physical address scrubbed. In
* case a scrub was aborted, this can
* be used to restart the scrub */
- __u64 unverified_errors; /* # of occurences where a read for a
+ __u64 unverified_errors; /* # of occurrences where a read for a
* full (64k) bio failed, but the re-
* check succeeded for each 4k piece.
* Intermittent error. */
@@ -271,7 +271,7 @@ struct btrfs_balance_args {
/* report balance progress to userspace */
struct btrfs_balance_progress {
__u64 expected; /* estimated # of chunks that will be
- * relocated to fulfill the request */
+ * relocated to fulfil the request */
__u64 considered; /* # of chunks we have considered so far */
__u64 completed; /* # of chunks relocated so far */
};
diff --git a/kerncompat.h b/kerncompat.h
index ee65aa7..378f055 100644
--- a/kerncompat.h
+++ b/kerncompat.h
@@ -72,7 +72,7 @@
static inline void print_trace(void)
{
void *array[MAX_BACKTRACE];
- size_t size;
+ int size;
size = backtrace(array, MAX_BACKTRACE);
backtrace_symbols_fd(array, size, 2);
@@ -334,12 +334,16 @@ struct __una_u32 { __le32 x; } __attribute__((__packed__));
struct __una_u64 { __le64 x; } __attribute__((__packed__));
#define get_unaligned_le8(p) (*((u8 *)(p)))
+#define get_unaligned_8(p) (*((u8 *)(p)))
#define put_unaligned_le8(val,p) ((*((u8 *)(p))) = (val))
#define get_unaligned_le16(p) le16_to_cpu(((const struct __una_u16 *)(p))->x)
+#define get_unaligned_16(p) (((const struct __una_u16 *)(p))->x)
#define put_unaligned_le16(val,p) (((struct __una_u16 *)(p))->x = cpu_to_le16(val))
#define get_unaligned_le32(p) le32_to_cpu(((const struct __una_u32 *)(p))->x)
+#define get_unaligned_32(p) (((const struct __una_u32 *)(p))->x)
#define put_unaligned_le32(val,p) (((struct __una_u32 *)(p))->x = cpu_to_le32(val))
#define get_unaligned_le64(p) le64_to_cpu(((const struct __una_u64 *)(p))->x)
+#define get_unaligned_64(p) (((const struct __una_u64 *)(p))->x)
#define put_unaligned_le64(val,p) (((struct __una_u64 *)(p))->x = cpu_to_le64(val))
#ifndef true
diff --git a/list_sort.c b/list_sort.c
index f526b40..9e2c202 100644
--- a/list_sort.c
+++ b/list_sort.c
@@ -1,5 +1,5 @@
/*
- * taken from linux kernel lib/list_sort.c, removed uneeded code and adapted
+ * taken from linux kernel lib/list_sort.c, removed unneeded code and adapted
* for btrfsprogs
*/
diff --git a/m4/ax_check_define.m4 b/m4/ax_check_define.m4
new file mode 100644
index 0000000..4bc6948
--- /dev/null
+++ b/m4/ax_check_define.m4
@@ -0,0 +1,92 @@
+# ===========================================================================
+# http://www.gnu.org/software/autoconf-archive/ax_check_define.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+# AC_CHECK_DEFINE([symbol], [ACTION-IF-FOUND], [ACTION-IF-NOT])
+# AX_CHECK_DEFINE([includes],[symbol], [ACTION-IF-FOUND], [ACTION-IF-NOT])
+#
+# DESCRIPTION
+#
+# Complements AC_CHECK_FUNC but it does not check for a function but for a
+# define to exist. Consider a usage like:
+#
+# AC_CHECK_DEFINE(__STRICT_ANSI__, CFLAGS="$CFLAGS -D_XOPEN_SOURCE=500")
+#
+# LICENSE
+#
+# Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 3 of the License, or (at your
+# option) any later version.
+#
+# 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, see <http://www.gnu.org/licenses/>.
+#
+# As a special exception, the respective Autoconf Macro's copyright owner
+# gives unlimited permission to copy, distribute and modify the configure
+# scripts that are the output of Autoconf when processing the Macro. You
+# need not follow the terms of the GNU General Public License when using
+# or distributing such scripts, even though portions of the text of the
+# Macro appear in them. The GNU General Public License (GPL) does govern
+# all other use of the material that constitutes the Autoconf Macro.
+#
+# This special exception to the GPL applies to versions of the Autoconf
+# Macro released by the Autoconf Archive. When you make and distribute a
+# modified version of the Autoconf Macro, you may extend this special
+# exception to the GPL to apply to your modified version as well.
+
+#serial 8
+
+AU_ALIAS([AC_CHECK_DEFINED], [AC_CHECK_DEFINE])
+AC_DEFUN([AC_CHECK_DEFINE],[
+AS_VAR_PUSHDEF([ac_var],[ac_cv_defined_$1])dnl
+AC_CACHE_CHECK([for $1 defined], ac_var,
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[
+ #ifdef $1
+ int ok;
+ #else
+ choke me
+ #endif
+]])],[AS_VAR_SET(ac_var, yes)],[AS_VAR_SET(ac_var, no)]))
+AS_IF([test AS_VAR_GET(ac_var) != "no"], [$2], [$3])dnl
+AS_VAR_POPDEF([ac_var])dnl
+])
+
+AU_ALIAS([AX_CHECK_DEFINED], [AX_CHECK_DEFINE])
+AC_DEFUN([AX_CHECK_DEFINE],[
+AS_VAR_PUSHDEF([ac_var],[ac_cv_defined_$2_$1])dnl
+AC_CACHE_CHECK([for $2 defined in $1], ac_var,
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <$1>]], [[
+ #ifdef $2
+ int ok;
+ #else
+ choke me
+ #endif
+]])],[AS_VAR_SET(ac_var, yes)],[AS_VAR_SET(ac_var, no)]))
+AS_IF([test AS_VAR_GET(ac_var) != "no"], [$3], [$4])dnl
+AS_VAR_POPDEF([ac_var])dnl
+])
+
+AC_DEFUN([AX_CHECK_FUNC],
+[AS_VAR_PUSHDEF([ac_var], [ac_cv_func_$2])dnl
+AC_CACHE_CHECK([for $2], ac_var,
+dnl AC_LANG_FUNC_LINK_TRY
+[AC_LINK_IFELSE([AC_LANG_PROGRAM([$1
+ #undef $2
+ char $2 ();],[
+ char (*f) () = $2;
+ return f != $2; ])],
+ [AS_VAR_SET(ac_var, yes)],
+ [AS_VAR_SET(ac_var, no)])])
+AS_IF([test AS_VAR_GET(ac_var) = yes], [$3], [$4])dnl
+AS_VAR_POPDEF([ac_var])dnl
+])# AC_CHECK_FUNC
diff --git a/mkfs.c b/mkfs.c
index 5e79e0b..697bdc2 100644
--- a/mkfs.c
+++ b/mkfs.c
@@ -88,7 +88,7 @@ static int create_metadata_block_groups(struct btrfs_root *root, int mixed,
BTRFS_BLOCK_GROUP_DATA);
if (ret == -ENOSPC) {
fprintf(stderr,
- "no space to alloc data/metadata chunk\n");
+ "no space to allocate data/metadata chunk\n");
goto err;
}
BUG_ON(ret);
@@ -104,7 +104,7 @@ static int create_metadata_block_groups(struct btrfs_root *root, int mixed,
&chunk_start, &chunk_size,
BTRFS_BLOCK_GROUP_METADATA);
if (ret == -ENOSPC) {
- fprintf(stderr, "no space to alloc metadata chunk\n");
+ fprintf(stderr, "no space to allocate metadata chunk\n");
goto err;
}
BUG_ON(ret);
@@ -136,7 +136,7 @@ static int create_data_block_groups(struct btrfs_trans_handle *trans,
&chunk_start, &chunk_size,
BTRFS_BLOCK_GROUP_DATA);
if (ret == -ENOSPC) {
- fprintf(stderr, "no space to alloc data chunk\n");
+ fprintf(stderr, "no space to allocate data chunk\n");
goto err;
}
BUG_ON(ret);
@@ -971,7 +971,7 @@ static int create_chunks(struct btrfs_trans_handle *trans,
size_of_data = minimum_data_chunk_size;
ret = btrfs_alloc_data_chunk(trans, root->fs_info->extent_root,
- &chunk_start, size_of_data, data_type);
+ &chunk_start, size_of_data, data_type, 0);
BUG_ON(ret);
ret = btrfs_make_block_group(trans, root->fs_info->extent_root, 0,
data_type, BTRFS_FIRST_CHUNK_TREE_OBJECTID,
@@ -1205,7 +1205,7 @@ static int is_temp_block_group(struct extent_buffer *node,
* 1) Empty chunk
* Temp chunk is always empty.
*
- * 2) profile dismatch with mkfs profile.
+ * 2) profile mismatch with mkfs profile.
* Temp chunk is always in SINGLE
*
* 3) Size differs with mkfs_alloc
@@ -1482,6 +1482,7 @@ int main(int argc, char **argv)
}
sectorsize = max(sectorsize, (u32)sysconf(_SC_PAGESIZE));
+ stripesize = sectorsize;
saved_optind = optind;
dev_cnt = argc - optind;
if (dev_cnt == 0)
@@ -1663,7 +1664,7 @@ int main(int argc, char **argv)
if (group_profile_max_safe_loss(metadata_profile) <
group_profile_max_safe_loss(data_profile)){
fprintf(stderr,
- "WARNING: metatdata has lower redundancy than data!\n\n");
+ "WARNING: metadata has lower redundancy than data!\n\n");
}
mkfs_cfg.label = label;
@@ -1675,7 +1676,7 @@ int main(int argc, char **argv)
mkfs_cfg.stripesize = stripesize;
mkfs_cfg.features = features;
- ret = make_btrfs(fd, &mkfs_cfg);
+ ret = make_btrfs(fd, &mkfs_cfg, NULL);
if (ret) {
fprintf(stderr, "error during mkfs: %s\n", strerror(-ret));
exit(1);
diff --git a/props.c b/props.c
index 5b74932..a7e3e96 100644
--- a/props.c
+++ b/props.c
@@ -48,16 +48,15 @@ static int prop_read_only(enum prop_object_type type,
fd = open(object, O_RDONLY);
if (fd < 0) {
ret = -errno;
- fprintf(stderr, "ERROR: open %s failed. %s\n",
- object, strerror(-ret));
+ error("failed to open %s: %s", object, 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 %s. %s\n",
- object, strerror(-ret));
+ error("failed to get flags for %s: %s", object,
+ strerror(-ret));
goto out;
}
@@ -76,15 +75,15 @@ static int prop_read_only(enum prop_object_type type,
flags = flags & ~BTRFS_SUBVOL_RDONLY;
} else {
ret = -EINVAL;
- fprintf(stderr, "ERROR: invalid value for property.\n");
+ error("invalid value for property: %s", value);
goto out;
}
ret = ioctl(fd, BTRFS_IOC_SUBVOL_SETFLAGS, &flags);
if (ret < 0) {
ret = -errno;
- fprintf(stderr, "ERROR: failed to set flags for %s. %s\n",
- object, strerror(-ret));
+ error("failed to set flags for %s: %s", object,
+ strerror(-ret));
goto out;
}
@@ -130,8 +129,7 @@ static int prop_compression(enum prop_object_type type,
fd = open_file_or_dir3(object, &dirstream, open_flags);
if (fd == -1) {
ret = -errno;
- fprintf(stderr, "ERROR: open %s failed. %s\n",
- object, strerror(-ret));
+ error("failed to open %s: %s", object, strerror(-ret));
goto out;
}
@@ -151,9 +149,8 @@ static int prop_compression(enum prop_object_type type,
if (sret < 0) {
ret = -errno;
if (ret != -ENOATTR)
- fprintf(stderr,
- "ERROR: failed to %s compression for %s. %s\n",
- value ? "set" : "get", object, strerror(-ret));
+ error("failed to %s compression for %s: %s",
+ value ? "set" : "get", object, strerror(-ret));
else
ret = 0;
goto out;
@@ -169,9 +166,8 @@ static int prop_compression(enum prop_object_type type,
sret = fgetxattr(fd, xattr_name, buf, len);
if (sret < 0) {
ret = -errno;
- fprintf(stderr,
- "ERROR: failed to get compression for %s. %s\n",
- object, strerror(-ret));
+ error("failed to get compression for %s: %s",
+ object, strerror(-ret));
goto out;
}
fprintf(stdout, "compression=%.*s\n", (int)len, buf);
diff --git a/qgroup-verify.c b/qgroup-verify.c
index 1a0d38c..6ca95eb 100644
--- a/qgroup-verify.c
+++ b/qgroup-verify.c
@@ -711,8 +711,13 @@ static void read_qgroup_status(struct btrfs_path *path,
status_item = btrfs_item_ptr(path->nodes[0], path->slots[0],
struct btrfs_qgroup_status_item);
flags = btrfs_qgroup_status_flags(path->nodes[0], status_item);
- counts->qgroup_inconsist = flags & BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT;
- counts->rescan_running = flags & BTRFS_QGROUP_STATUS_FLAG_RESCAN;
+ /*
+ * Since qgroup_inconsist/rescan_running is just one bit,
+ * assign value directly won't work.
+ */
+ counts->qgroup_inconsist = !!(flags &
+ BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT);
+ counts->rescan_running = !!(flags & BTRFS_QGROUP_STATUS_FLAG_RESCAN);
}
static int load_quota_info(struct btrfs_fs_info *info)
@@ -1095,6 +1100,19 @@ int report_qgroups(int all)
return ret;
}
+void free_qgroup_counts(void)
+{
+ struct rb_node *node;
+ struct qgroup_count *c;
+ node = rb_first(&counts.root);
+ while (node) {
+ c = rb_entry(node, struct qgroup_count, rb_node);
+ node = rb_next(node);
+ rb_erase(&c->rb_node, &counts.root);
+ free(c);
+ }
+}
+
int qgroup_verify_all(struct btrfs_fs_info *info)
{
int ret;
diff --git a/qgroup-verify.h b/qgroup-verify.h
index 3747465..0f8ff9b 100644
--- a/qgroup-verify.h
+++ b/qgroup-verify.h
@@ -27,4 +27,6 @@ int report_qgroups(int all);
int print_extent_state(struct btrfs_fs_info *info, u64 subvol);
+void free_qgroup_counts(void);
+
#endif
diff --git a/qgroup.c b/qgroup.c
index a672ac0..f17fdae 100644
--- a/qgroup.c
+++ b/qgroup.c
@@ -1094,7 +1094,8 @@ static int __qgroups_search(int fd, struct qgroup_lookup *qgroup_lookup)
off);
off += sizeof(*sh);
- if (sh->type == BTRFS_QGROUP_STATUS_KEY) {
+ if (btrfs_search_header_type(sh)
+ == BTRFS_QGROUP_STATUS_KEY) {
struct btrfs_qgroup_status_item *si;
u64 flags;
@@ -1102,7 +1103,8 @@ static int __qgroups_search(int fd, struct qgroup_lookup *qgroup_lookup)
(args.buf + off);
flags = btrfs_stack_qgroup_status_flags(si);
print_status_flag_warning(flags);
- } else if (sh->type == BTRFS_QGROUP_INFO_KEY) {
+ } else if (btrfs_search_header_type(sh)
+ == BTRFS_QGROUP_INFO_KEY) {
info = (struct btrfs_qgroup_info_item *)
(args.buf + off);
a1 = btrfs_stack_qgroup_info_generation(info);
@@ -1114,10 +1116,12 @@ static int __qgroups_search(int fd, struct qgroup_lookup *qgroup_lookup)
a5 =
btrfs_stack_qgroup_info_exclusive_compressed
(info);
- add_qgroup(qgroup_lookup, sh->offset, a1, a2,
- a3, a4, a5, 0, 0, 0, 0, 0,
- NULL, NULL);
- } else if (sh->type == BTRFS_QGROUP_LIMIT_KEY) {
+ add_qgroup(qgroup_lookup,
+ btrfs_search_header_offset(sh), a1,
+ a2, a3, a4, a5, 0, 0, 0, 0, 0, NULL,
+ NULL);
+ } else if (btrfs_search_header_type(sh)
+ == BTRFS_QGROUP_LIMIT_KEY) {
limit = (struct btrfs_qgroup_limit_item *)
(args.buf + off);
@@ -1130,34 +1134,38 @@ static int __qgroups_search(int fd, struct qgroup_lookup *qgroup_lookup)
(limit);
a5 = btrfs_stack_qgroup_limit_rsv_exclusive
(limit);
- add_qgroup(qgroup_lookup, sh->offset, 0, 0,
- 0, 0, 0, a1, a2, a3, a4, a5,
+ add_qgroup(qgroup_lookup,
+ btrfs_search_header_offset(sh), 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)
+ } else if (btrfs_search_header_type(sh)
+ == BTRFS_QGROUP_RELATION_KEY) {
+ if (btrfs_search_header_offset(sh)
+ < btrfs_search_header_objectid(sh))
goto skip;
bq = qgroup_tree_search(qgroup_lookup,
- sh->offset);
+ btrfs_search_header_offset(sh));
if (!bq)
goto skip;
bq1 = qgroup_tree_search(qgroup_lookup,
- sh->objectid);
+ btrfs_search_header_objectid(sh));
if (!bq1)
goto skip;
- add_qgroup(qgroup_lookup, sh->offset, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, bq, bq1);
+ add_qgroup(qgroup_lookup,
+ btrfs_search_header_offset(sh), 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, bq, bq1);
} else
goto done;
skip:
- off += sh->len;
+ off += btrfs_search_header_len(sh);
/*
* record the mins in sk so we can make sure the
* next search doesn't repeat this root
*/
- sk->min_type = sh->type;
- sk->min_offset = sh->offset;
- sk->min_objectid = sh->objectid;
+ sk->min_type = btrfs_search_header_type(sh);
+ sk->min_offset = btrfs_search_header_offset(sh);
+ sk->min_objectid = btrfs_search_header_objectid(sh);
}
sk->nr_items = 4096;
/*
diff --git a/send-utils.c b/send-utils.c
index 3c369b8..a85fa08 100644
--- a/send-utils.c
+++ b/send-utils.c
@@ -99,25 +99,25 @@ static int btrfs_read_root_item_raw(int mnt_fd, u64 root_id, size_t buf_len,
off += sizeof(*sh);
item = (struct btrfs_root_item *)(args.buf + off);
- off += sh->len;
+ off += btrfs_search_header_len(sh);
- sk->min_objectid = sh->objectid;
- sk->min_type = sh->type;
- sk->min_offset = sh->offset;
+ sk->min_objectid = btrfs_search_header_objectid(sh);
+ sk->min_type = btrfs_search_header_type(sh);
+ sk->min_offset = btrfs_search_header_offset(sh);
- if (sh->objectid > root_id)
+ if (btrfs_search_header_objectid(sh) > root_id)
break;
- if (sh->objectid == root_id &&
- sh->type == BTRFS_ROOT_ITEM_KEY) {
- if (sh->len > buf_len) {
+ if (btrfs_search_header_objectid(sh) == root_id &&
+ btrfs_search_header_type(sh) == BTRFS_ROOT_ITEM_KEY) {
+ if (btrfs_search_header_len(sh) > buf_len) {
/* btrfs-progs is too old for kernel */
fprintf(stderr,
"ERROR: buf for read_root_item_raw() is too small, get newer btrfs tools!\n");
return -EOVERFLOW;
}
- memcpy(buf, item, sh->len);
- *read_len = sh->len;
+ memcpy(buf, item, btrfs_search_header_len(sh));
+ *read_len = btrfs_search_header_len(sh);
found = 1;
}
}
@@ -280,11 +280,12 @@ static int btrfs_subvolid_resolve_sub(int fd, char *path, size_t *path_len,
}
search_header = (struct btrfs_ioctl_search_header *)search_arg.buf;
backref_item = (struct btrfs_root_ref *)(search_header + 1);
- if (search_header->offset != BTRFS_FS_TREE_OBJECTID) {
+ if (btrfs_search_header_offset(search_header)
+ != BTRFS_FS_TREE_OBJECTID) {
int sub_ret;
sub_ret = btrfs_subvolid_resolve_sub(fd, path, path_len,
- search_header->offset);
+ btrfs_search_header_offset(search_header));
if (sub_ret)
return sub_ret;
if (*path_len < 1)
@@ -298,7 +299,8 @@ static int btrfs_subvolid_resolve_sub(int fd, char *path, size_t *path_len,
int len;
memset(&ino_lookup_arg, 0, sizeof(ino_lookup_arg));
- ino_lookup_arg.treeid = search_header->offset;
+ ino_lookup_arg.treeid =
+ btrfs_search_header_offset(search_header);
ino_lookup_arg.objectid =
btrfs_stack_root_ref_dirid(backref_item);
ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &ino_lookup_arg);
@@ -401,7 +403,7 @@ static struct subvol_info *tree_search(struct rb_root *root,
}
/*
- * this function will be only called if kernel dosen't support uuid tree.
+ * this function will be only called if kernel doesn't support uuid tree.
*/
static struct subvol_info *subvol_uuid_search_old(struct subvol_uuid_search *s,
u64 root_id, const u8 *uuid, u64 transid,
@@ -593,14 +595,19 @@ int subvol_uuid_search_init(int mnt_fd, struct subvol_uuid_search *s)
off);
off += sizeof(*sh);
- if ((sh->objectid != 5 &&
- sh->objectid < BTRFS_FIRST_FREE_OBJECTID) ||
- sh->objectid > BTRFS_LAST_FREE_OBJECTID)
+ if ((btrfs_search_header_objectid(sh) != 5 &&
+ btrfs_search_header_objectid(sh)
+ < BTRFS_FIRST_FREE_OBJECTID) ||
+ btrfs_search_header_objectid(sh)
+ > BTRFS_LAST_FREE_OBJECTID) {
goto skip;
+ }
- if (sh->type == BTRFS_ROOT_ITEM_KEY) {
+ if (btrfs_search_header_type(sh)
+ == BTRFS_ROOT_ITEM_KEY) {
/* older kernels don't have uuids+times */
- if (sh->len < sizeof(root_item)) {
+ if (btrfs_search_header_len(sh)
+ < sizeof(root_item)) {
root_item_valid = 0;
goto skip;
}
@@ -609,13 +616,14 @@ int subvol_uuid_search_init(int mnt_fd, struct subvol_uuid_search *s)
memcpy(&root_item, root_item_ptr,
sizeof(root_item));
root_item_valid = 1;
- } else if (sh->type == BTRFS_ROOT_BACKREF_KEY ||
+ } else if (btrfs_search_header_type(sh)
+ == BTRFS_ROOT_BACKREF_KEY ||
root_item_valid) {
if (!root_item_valid)
goto skip;
path = btrfs_list_path_for_root(mnt_fd,
- sh->objectid);
+ btrfs_search_header_objectid(sh));
if (!path)
path = strdup("");
if (IS_ERR(path)) {
@@ -623,12 +631,12 @@ int subvol_uuid_search_init(int mnt_fd, struct subvol_uuid_search *s)
fprintf(stderr, "ERROR: unable to "
"resolve path "
"for root %llu\n",
- sh->objectid);
+ btrfs_search_header_objectid(sh));
goto out;
}
si = calloc(1, sizeof(*si));
- si->root_id = sh->objectid;
+ si->root_id = btrfs_search_header_objectid(sh);
memcpy(si->uuid, root_item.uuid,
BTRFS_UUID_SIZE);
memcpy(si->parent_uuid, root_item.parent_uuid,
@@ -648,15 +656,15 @@ int subvol_uuid_search_init(int mnt_fd, struct subvol_uuid_search *s)
}
skip:
- off += sh->len;
+ off += btrfs_search_header_len(sh);
/*
* record the mins in sk so we can make sure the
* next search doesn't repeat this root
*/
- sk->min_objectid = sh->objectid;
- sk->min_offset = sh->offset;
- sk->min_type = sh->type;
+ sk->min_objectid = btrfs_search_header_objectid(sh);
+ sk->min_offset = btrfs_search_header_offset(sh);
+ sk->min_type = btrfs_search_header_type(sh);
}
sk->nr_items = 4096;
if (sk->min_offset < (u64)-1)
diff --git a/show-blocks b/show-blocks
index 0164be9..49e1770 100755
--- a/show-blocks
+++ b/show-blocks
@@ -224,7 +224,7 @@ elif options.input_file:
shapeit(data)
# try to drop out the least common data points by creating
-# a historgram of the sectors seen.
+# a histogram of the sectors seen.
sectors = data[:,0]
sizes = data[:,1]
datalen = len(data)
@@ -237,7 +237,7 @@ bytes_per_cell = byte_range / total_cells
f = figure(figsize=(8,6))
-# Throughput goes at the botoom
+# Throughput goes at the bottom
a = subplot(1, 1, 1)
subplots_adjust(right=0.7)
datai = 0
diff --git a/super-recover.c b/super-recover.c
index e2c3129..88ecdee 100644
--- a/super-recover.c
+++ b/super-recover.c
@@ -316,7 +316,7 @@ int btrfs_recover_superblocks(const char *dname,
ret = 3;
goto no_recover;
}
- /* reset super_bytenr in order that we will rewite all supers */
+ /* reset super_bytenr in order that we will rewrite all supers */
root->fs_info->super_bytenr = BTRFS_SUPER_INFO_OFFSET;
ret = write_all_supers(root);
if (!ret)
@@ -328,7 +328,7 @@ int btrfs_recover_superblocks(const char *dname,
no_recover:
recover_err_str(ret);
free_recover_superblock(&recover);
- /* check if we have freed fs_deivces in close_ctree() */
+ /* check if we have freed fs_devices in close_ctree() */
if (!root)
btrfs_close_devices(recover.fs_devices);
return ret;
diff --git a/tests/README.md b/tests/README.md
index be3bda8..6bb3de4 100644
--- a/tests/README.md
+++ b/tests/README.md
@@ -1,5 +1,14 @@
# Btrfs-progs tests
+A testsuite covering functionality of btrfs-progs, ie. the checker, image, mkfs
+and similar tools. There are no special requirements on kernel features, the
+tests build on top of the core functionality like snapshots and device
+management. In some cases optional features are turned on by mkfs and the
+filesystem image could be mounted, such tests might fail if there's lack of
+support.
+
+## Quick start
+
Run the tests from the top directory:
```shell
@@ -20,7 +29,7 @@ category, eg. `fsck-tests-results.txt`.
## Selective testing
-The test are prefixed by a number for ordering and uniquenes. To run a
+The test are prefixed by a number for ordering and uniqueness. To run a
particular test use:
```shell
@@ -54,14 +63,22 @@ will run the first test in fsck-tests subdirectory.
* tests that are supposed to run various utilities on the images and not
crash
+*tests/cli-tests/:*
+
+ * tests for command line interface, option coverage, weird option combinations that should not work
+ * not necessary to do any functional testing, could be rather lightweight
+ * functional tests should go to to other test dirs
+ * the driver script will only execute `./test.sh` in the test directory
+
*tests/misc-tests/:*
* anything that does not fit to the above, the test driver script will only
execute `./test.sh` in the test directory
*tests/common:*
+*tests/common.convert:*
- * script with helpers
+ * script with shell helpers, separated by functionality
*tests/test.img:*
@@ -92,7 +109,8 @@ the root helper).
### Verbosity
Setting the variable `TEST_LOG=tty` will print all commands executed by some of
-the wrappers (`run_check` etc), other commands are silent.
+the wrappers (`run_check` etc), other commands are not printed to the terminal
+(but the full output is in the log).
### Permissions
@@ -111,26 +129,33 @@ least the mounts and loop devices need to be cleaned before the next run.
This is partially done by the script `clean-tests.sh`, you may want to check
the loop devices as they are managed on a per-test basis.
+### Prototyping tests, quick tests
+
+There's a script `test-console.sh` that will run shell commands in a loop and
+logs the output with the testing environment set up.
+
## New test
1. Pick the category for the new test or fallback to `misc-tests` if not sure. For
an easy start copy an existing `test.sh` script from some test that might be
-close to the purpose of your new test.
+close to the purpose of your new test. The environment setup includes the
+common scripts and/or prepares the test devices. Other scripts contain examples
+how to do mkfs, mount, unmount, check, etc.
-* Use the highest unused number in the sequence, write a short descriptive title
-and join by dashes `-`.
+2. Use the highest unused number in the sequence, write a short descriptive title
+and join by dashes `-`. This will become the directory name, eg. `012-subvolume-sync-must-wait`.
-* Write a short description of the bug and how it's teste to the comment at the
-begining of `test.sh`.
+3. Write a short description of the bug and how it's tested to the comment at the
+begining of `test.sh`. You don't need to add the file to git yet.
-* Write the test commands, comment anything that's not obvious.
+4. Write the test commands, comment anything that's not obvious.
-* Test your test. Use the `TEST` variable to jump right to your test:
+5. Test your test. Use the `TEST` variable to jump right to your test:
```shell
$ make TEST=012\* tests-misc # from top directory
$ TEST=012\* ./misc-tests.sh # from tests/
```
-* The commit changelog should reference a commit that either introduced or
+6. The commit changelog should reference a commit that either introduced or
fixed the bug (or both). Subject line of the shall mention the name of the
new directory for ease of search, eg. `btrfs-progs: tests: add 012-subvolume-sync-must-wait`
diff --git a/tests/cli-tests.sh b/tests/cli-tests.sh
index e65e7f5..72f7865 100755
--- a/tests/cli-tests.sh
+++ b/tests/cli-tests.sh
@@ -2,8 +2,6 @@
#
# command line interface coverage tests
-unset TOP
-unset LANG
LANG=C
SCRIPT_DIR=$(dirname $(readlink -f $0))
TOP=$(readlink -f $SCRIPT_DIR/../)
@@ -17,6 +15,7 @@ export TOP
export RESULTS
export LANG
export IMAGE
+export TEST_DEV
rm -f $RESULTS
diff --git a/tests/cli-tests/003-fi-resize-args/test.sh b/tests/cli-tests/003-fi-resize-args/test.sh
new file mode 100755
index 0000000..2f136fa
--- /dev/null
+++ b/tests/cli-tests/003-fi-resize-args/test.sh
@@ -0,0 +1,46 @@
+#!/bin/bash
+#
+# test parsing of various resize arguments
+
+source $TOP/tests/common
+
+check_prereq mkfs.btrfs
+check_prereq btrfs
+
+setup_root_helper
+prepare_test_dev 2g
+
+run_check $TOP/mkfs.btrfs -f $IMAGE
+run_check_mount_test_dev
+
+# missing the one of the required arguments
+for sep in '' '--'; do
+ run_check_stdout $TOP/btrfs filesystem resize $sep |
+ grep -q "btrfs filesystem resize: too few arguments"
+ run_check_stdout $TOP/btrfs filesystem resize $sep $TEST_MNT |
+ grep -q "btrfs filesystem resize: too few arguments"
+ run_check_stdout $TOP/btrfs filesystem resize $sep -128M |
+ grep -q "btrfs filesystem resize: too few arguments"
+ run_check_stdout $TOP/btrfs filesystem resize $sep +128M |
+ grep -q "btrfs filesystem resize: too few arguments"
+ run_check_stdout $TOP/btrfs filesystem resize $sep 512M |
+ grep -q "btrfs filesystem resize: too few arguments"
+ run_check_stdout $TOP/btrfs filesystem resize $sep 1:-128M |
+ grep -q "btrfs filesystem resize: too few arguments"
+ run_check_stdout $TOP/btrfs filesystem resize $sep 1:512M |
+ grep -q "btrfs filesystem resize: too few arguments"
+ run_check_stdout $TOP/btrfs filesystem resize $sep 1:+128M |
+ grep -q "btrfs filesystem resize: too few arguments"
+done
+
+# valid resize
+for sep in '' '--'; do
+ run_check $SUDO_HELPER $TOP/btrfs filesystem resize $sep -128M $TEST_MNT
+ run_check $SUDO_HELPER $TOP/btrfs filesystem resize $sep +128M $TEST_MNT
+ run_check $SUDO_HELPER $TOP/btrfs filesystem resize $sep 512M $TEST_MNT
+ run_check $SUDO_HELPER $TOP/btrfs filesystem resize $sep 1:-128M $TEST_MNT
+ run_check $SUDO_HELPER $TOP/btrfs filesystem resize $sep 1:512M $TEST_MNT
+ run_check $SUDO_HELPER $TOP/btrfs filesystem resize $sep 1:+128M $TEST_MNT
+done
+
+run_check_umount_test_dev
diff --git a/tests/common b/tests/common
index 91682ef..c50b661 100644
--- a/tests/common
+++ b/tests/common
@@ -90,7 +90,15 @@ run_mustfail()
check_prereq()
{
if ! [ -f $TOP/$1 ]; then
- _fail "Failed prerequisities: $1";
+ _fail "Failed prerequisites: $1";
+ fi
+}
+
+check_global_prereq()
+{
+ which $1 &> /dev/null
+ if [ $? -ne 0 ]; then
+ _fail "Failed system wide prerequisities: $1";
fi
}
diff --git a/tests/common.convert b/tests/common.convert
new file mode 100644
index 0000000..4e3d49c
--- /dev/null
+++ b/tests/common.convert
@@ -0,0 +1,182 @@
+#!/bin/bash
+# helpers for btrfs-convert tests
+
+# how many files to create.
+DATASET_SIZE=50
+
+generate_dataset() {
+
+ dataset_type="$1"
+ dirpath=$TEST_MNT/$dataset_type
+ run_check $SUDO_HELPER mkdir -p $dirpath
+
+ case $dataset_type in
+ small)
+ for num in $(seq 1 $DATASET_SIZE); do
+ run_check $SUDO_HELPER dd if=/dev/urandom of=$dirpath/$dataset_type.$num bs=10K \
+ count=1 >/dev/null 2>&1
+ done
+ ;;
+
+ hardlink)
+ for num in $(seq 1 $DATASET_SIZE); do
+ run_check $SUDO_HELPER touch $dirpath/$dataset_type.$num
+ run_check $SUDO_HELPER ln $dirpath/$dataset_type.$num $dirpath/hlink.$num
+ done
+ ;;
+
+ symlink)
+ for num in $(seq 1 $DATASET_SIZE); do
+ run_check $SUDO_HELPER touch $dirpath/$dataset_type.$num
+ run_check $SUDO_HELPER ln -s $dirpath/$dataset_type.$num $dirpath/slink.$num
+ done
+ ;;
+
+ brokenlink)
+ for num in $(seq 1 $DATASET_SIZE); do
+ run_check $SUDO_HELPER ln -s $dirpath/$dataset_type.$num $dirpath/blink.$num
+ done
+ ;;
+
+ perm)
+ for modes in 777 775 755 750 700 666 664 644 640 600 444 440 400 000 \
+ 1777 1775 1755 1750 1700 1666 1664 1644 1640 1600 1444 1440 1400 1000 \
+ 2777 2775 2755 2750 2700 2666 2664 2644 2640 2600 2444 2440 2400 2000 \
+ 4777 4775 4755 4750 4700 4666 4664 4644 4640 4600 4444 4440 4400 4000; do
+ if [[ "$modes" == *9* ]] || [[ "$modes" == *8* ]]
+ then
+ continue;
+ else
+ run_check $SUDO_HELPER touch $dirpath/$dataset_type.$modes
+ run_check $SUDO_HELPER chmod $modes $dirpath/$dataset_type.$modes
+ fi
+ done
+ ;;
+
+ sparse)
+ for num in $(seq 1 $DATASET_SIZE); do
+ run_check $SUDO_HELPER dd if=/dev/urandom of=$dirpath/$dataset_type.$num bs=10K \
+ count=1 >/dev/null 2>&1
+ run_check $SUDO_HELPER truncate -s 500K $dirpath/$dataset_type.$num
+ run_check $SUDO_HELPER dd if=/dev/urandom of=$dirpath/$dataset_type.$num bs=10K \
+ oflag=append conv=notrunc count=1 >/dev/null 2>&1
+ run_check $SUDO_HELPER truncate -s 800K $dirpath/$dataset_type.$num
+ done
+ ;;
+
+ acls)
+ for num in $(seq 1 $DATASET_SIZE); do
+ run_check $SUDO_HELPER touch $dirpath/$dataset_type.$num
+ run_check $SUDO_HELPER setfacl -m "u:root:x" $dirpath/$dataset_type.$num
+ run_check $SUDO_HELPER setfattr -n user.foo -v bar$num $dirpath/$dataset_type.$num
+ done
+ ;;
+ esac
+}
+
+populate_fs() {
+
+ for dataset_type in 'small' 'hardlink' 'symlink' 'brokenlink' 'perm' 'sparse' 'acls'; do
+ generate_dataset "$dataset_type"
+ done
+}
+
+# verbose message before the test, same arguments as convert_test
+convert_test_preamble() {
+ local features
+ local msg
+
+ features="$1"
+ msg="$2"
+ shift 3
+ echo " [TEST/conv] $msg, btrfs" "${features:-defaults}"
+ echo "creating ext image with: $@" >> $RESULTS
+}
+
+# prepare TEST_DEV before conversion, create filesystem and mount it, image
+# size is 512MB
+# $@: free form, command to create the filesystem, with appended -F
+convert_test_prep_fs() {
+ # TEST_DEV not removed as the file might have special permissions, eg.
+ # when test image is on NFS and would not be writable for root
+ run_check truncate -s 0 $TEST_DEV
+ # 256MB is the smallest acceptable btrfs image.
+ run_check truncate -s 512M $TEST_DEV
+ run_check "$@" -F $TEST_DEV
+
+ # create a file to check btrfs-convert can convert regular file correct
+ run_check_mount_test_dev
+
+ # create a file inside the fs before convert, to make sure there is
+ # data covering btrfs backup superblock range (64M)
+ run_check $SUDO_HELPER dd if=/dev/zero bs=1M count=64 \
+ of=$TEST_MNT/convert_space_holder
+}
+
+# generate md5 checksums of files on $TEST_MNT
+# $1: path where the checksums will be stored
+convert_test_gen_checksums() {
+ local CHECKSUMTMP
+ CHECKSUMTMP="$1"
+
+ run_check $SUDO_HELPER dd if=/dev/zero of=$TEST_MNT/test bs=$nodesize \
+ count=1 >/dev/null 2>&1
+ run_check_stdout $SUDO_HELPER find $TEST_MNT -type f ! -name 'image' -exec md5sum {} \+ > "$CHECKSUMTMP"
+}
+
+# do conversion with given features and nodesize, fsck afterwards
+# $1: features, argument of -O, can be empty
+# $2: nodesize, argument of -N, can be empty
+convert_test_do_convert() {
+ run_check $TOP/btrfs-convert ${1:+-O "$1"} ${2:+-N "$2"} $TEST_DEV
+ run_check $TOP/btrfs check $TEST_DEV
+ run_check $TOP/btrfs-show-super -Ffa $TEST_DEV
+}
+
+# post conversion checks, verify md5sums
+# $1: file with checksums
+convert_test_post_check() {
+ local CHECKSUMTMP
+ CHECKSUMTMP="$1"
+
+ run_check_mount_test_dev
+ run_check_stdout $SUDO_HELPER md5sum -c "$CHECKSUMTMP" |
+ grep -q 'FAILED' && _fail "file validation failed"
+ run_check_umount_test_dev
+}
+
+# do rollback and fsck
+convert_test_post_rollback() {
+ run_check $TOP/btrfs-convert --rollback $TEST_DEV
+ run_check fsck -n -t ext2,ext3,ext4 $TEST_DEV
+}
+
+# simple wrapper for a convert test
+# $1: btrfs features, argument to -O
+# $2: description of the test "ext2 8k nodesize"
+# $3: nodesize value
+# $4 + rest: command to create the ext2 image
+convert_test() {
+ local features
+ local nodesize
+ local msg
+ local CHECKSUMTMP
+
+ features="$1"
+ msg="$2"
+ nodesize="$3"
+ shift 3
+ convert_test_preamble "$features" "$msg" "$nodesize" "$@"
+ convert_test_prep_fs "$@"
+ populate_fs
+ CHECKSUMTMP=$(mktemp --tmpdir btrfs-progs-convert.XXXXXXXXXX)
+ convert_test_gen_checksums "$CHECKSUMTMP"
+
+ run_check_umount_test_dev
+
+ convert_test_do_convert "$features" "$nodesize"
+ convert_test_post_check "$CHECKSUMTMP"
+ rm $CHECKSUMTMP
+
+ convert_test_post_rollback
+}
diff --git a/tests/convert-tests.sh b/tests/convert-tests.sh
index 06d8419..0e025f9 100755
--- a/tests/convert-tests.sh
+++ b/tests/convert-tests.sh
@@ -2,165 +2,48 @@
#
# convert ext2/3/4 images to btrfs images, and make sure the results are
# clean.
-#
-unset TOP
-unset LANG
LANG=C
SCRIPT_DIR=$(dirname $(readlink -f $0))
TOP=$(readlink -f $SCRIPT_DIR/../)
+TEST_DEV=${TEST_DEV:-}
RESULTS="$TOP/tests/convert-tests-results.txt"
-# how many files to create.
-DATASET_SIZE=50
+IMAGE="$TOP/tests/test.img"
source $TOP/tests/common
+source $TOP/tests/common.convert
-rm -f $RESULTS
-
-setup_root_helper
-prepare_test_dev 512M
-
-CHECKSUMTMP=$(mktemp --tmpdir btrfs-progs-convert.XXXXXXXXXX)
-
-generate_dataset() {
-
- dataset_type="$1"
- dirpath=$TEST_MNT/$dataset_type
- run_check $SUDO_HELPER mkdir -p $dirpath
-
- case $dataset_type in
- small)
- for num in $(seq 1 $DATASET_SIZE); do
- run_check $SUDO_HELPER dd if=/dev/urandom of=$dirpath/$dataset_type.$num bs=10K \
- count=1 >/dev/null 2>&1
- done
- ;;
-
- hardlink)
- for num in $(seq 1 $DATASET_SIZE); do
- run_check $SUDO_HELPER touch $dirpath/$dataset_type.$num
- run_check $SUDO_HELPER ln $dirpath/$dataset_type.$num $dirpath/hlink.$num
- done
- ;;
-
- symlink)
- for num in $(seq 1 $DATASET_SIZE); do
- run_check $SUDO_HELPER touch $dirpath/$dataset_type.$num
- run_check $SUDO_HELPER ln -s $dirpath/$dataset_type.$num $dirpath/slink.$num
- done
- ;;
-
- brokenlink)
- for num in $(seq 1 $DATASET_SIZE); do
- run_check $SUDO_HELPER ln -s $dirpath/$dataset_type.$num $dirpath/blink.$num
- done
- ;;
-
- perm)
- for modes in 777 775 755 750 700 666 664 644 640 600 444 440 400 000 \
- 1777 1775 1755 1750 1700 1666 1664 1644 1640 1600 1444 1440 1400 1000 \
- 2777 2775 2755 2750 2700 2666 2664 2644 2640 2600 2444 2440 2400 2000 \
- 4777 4775 4755 4750 4700 4666 4664 4644 4640 4600 4444 4440 4400 4000; do
- if [[ "$modes" == *9* ]] || [[ "$modes" == *8* ]]
- then
- continue;
- else
- run_check $SUDO_HELPER touch $dirpath/$dataset_type.$modes
- run_check $SUDO_HELPER chmod $modes $dirpath/$dataset_type.$modes
- fi
- done
- ;;
-
- sparse)
- for num in $(seq 1 $DATASET_SIZE); do
- run_check $SUDO_HELPER dd if=/dev/urandom of=$dirpath/$dataset_type.$num bs=10K \
- count=1 >/dev/null 2>&1
- run_check $SUDO_HELPER truncate -s 500K $dirpath/$dataset_type.$num
- run_check $SUDO_HELPER dd if=/dev/urandom of=$dirpath/$dataset_type.$num bs=10K \
- oflag=append conv=notrunc count=1 >/dev/null 2>&1
- run_check $SUDO_HELPER truncate -s 800K $dirpath/$dataset_type.$num
- done
- ;;
+export TOP
+export RESULTS
+export LANG
+export IMAGE
+export TEST_DEV
- acls)
- for num in $(seq 1 $DATASET_SIZE); do
- run_check $SUDO_HELPER touch $dirpath/$dataset_type.$num
- run_check $SUDO_HELPER setfacl -m "u:root:x" $dirpath/$dataset_type.$num
- run_check $SUDO_HELPER setfattr -n user.foo -v bar$num $dirpath/$dataset_type.$num
- done
- ;;
- esac
-}
-
-populate_fs() {
-
- for dataset_type in 'small' 'hardlink' 'symlink' 'brokenlink' 'perm' 'sparse' 'acls'; do
- generate_dataset "$dataset_type"
- done
-}
-
-convert_test() {
- local features
- local nodesize
-
- features="$1"
- shift
+rm -f $RESULTS
- if [ -z "$features" ]; then
- echo " [TEST/conv] $1, btrfs defaults"
+run_one_test() {
+ local testdir
+ local testname
+
+ testdir="$1"
+ testname=$(basename "$testdir")
+ echo " [TEST/conv] $testname"
+ cd "$testdir"
+ echo "=== Entering $testname" >> $RESULTS
+ if [ -x test.sh ]; then
+ # Only support custom test scripts
+ ./test.sh
+ if [ $? -ne 0 ]; then
+ _fail "test failed for case $testname"
+ fi
else
- echo " [TEST/conv] $1, btrfs $features"
+ _fail "custom test script not found"
fi
- nodesize=$2
- shift 2
- echo "creating ext image with: $*" >> $RESULTS
- # TEST_DEV not removed as the file might have special permissions, eg.
- # when test image is on NFS and would not be writable for root
- run_check truncate -s 0 $TEST_DEV
- # 256MB is the smallest acceptable btrfs image.
- run_check truncate -s 512M $TEST_DEV
- run_check $* -F $TEST_DEV
-
- # create a file to check btrfs-convert can convert regular file
- # correct
- run_check_mount_test_dev
- populate_fs
- run_check $SUDO_HELPER dd if=/dev/zero of=$TEST_MNT/test bs=$nodesize \
- count=1 >/dev/null 2>&1
- run_check_stdout find $TEST_MNT -type f ! -name 'image' -exec md5sum {} \+ > $CHECKSUMTMP
- run_check_umount_test_dev
-
- run_check $TOP/btrfs-convert ${features:+-O "$features"} -N "$nodesize" $TEST_DEV
- run_check $TOP/btrfs check $TEST_DEV
- run_check $TOP/btrfs-show-super $TEST_DEV
-
- run_check_mount_test_dev
- run_check_stdout md5sum -c $CHECKSUMTMP |
- grep -q 'FAILED' && _fail "file validation failed."
- run_check_umount_test_dev
}
-if ! [ -z "$TEST" ]; then
- echo " [TEST/conv] skipped all convert tests, TEST=$TEST"
- exit 0
-fi
-
-for feature in '' 'extref' 'skinny-metadata' 'no-holes'; do
- convert_test "$feature" "ext2 4k nodesize" 4096 mke2fs -b 4096
- convert_test "$feature" "ext3 4k nodesize" 4096 mke2fs -j -b 4096
- convert_test "$feature" "ext4 4k nodesize" 4096 mke2fs -t ext4 -b 4096
- convert_test "$feature" "ext2 8k nodesize" 8192 mke2fs -b 4096
- convert_test "$feature" "ext3 8k nodesize" 8192 mke2fs -j -b 4096
- convert_test "$feature" "ext4 8k nodesize" 8192 mke2fs -t ext4 -b 4096
- convert_test "$feature" "ext2 16k nodesize" 16384 mke2fs -b 4096
- convert_test "$feature" "ext3 16k nodesize" 16384 mke2fs -j -b 4096
- convert_test "$feature" "ext4 16k nodesize" 16384 mke2fs -t ext4 -b 4096
- convert_test "$feature" "ext2 32k nodesize" 32768 mke2fs -b 4096
- convert_test "$feature" "ext3 32k nodesize" 32768 mke2fs -j -b 4096
- convert_test "$feature" "ext4 32k nodesize" 32768 mke2fs -t ext4 -b 4096
- convert_test "$feature" "ext2 64k nodesize" 65536 mke2fs -b 4096
- convert_test "$feature" "ext3 64k nodesize" 65536 mke2fs -j -b 4096
- convert_test "$feature" "ext4 64k nodesize" 65536 mke2fs -t ext4 -b 4096
+# Test special images
+for i in $(find $TOP/tests/convert-tests -maxdepth 1 -mindepth 1 -type d \
+ ${TEST:+-name "$TEST"} | sort)
+do
+ run_one_test "$i"
done
-
-rm $CHECKSUMTMP
diff --git a/tests/convert-tests/001-ext2-basic/test.sh b/tests/convert-tests/001-ext2-basic/test.sh
new file mode 100755
index 0000000..8f4f935
--- /dev/null
+++ b/tests/convert-tests/001-ext2-basic/test.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+source $TOP/tests/common
+source $TOP/tests/common.convert
+
+setup_root_helper
+prepare_test_dev 512M
+check_prereq btrfs-convert
+
+for feature in '' 'extref' 'skinny-metadata' 'no-holes'; do
+ convert_test "$feature" "ext2 4k nodesize" 4096 mke2fs -b 4096
+ convert_test "$feature" "ext2 8k nodesize" 8192 mke2fs -b 4096
+ convert_test "$feature" "ext2 16k nodesize" 16384 mke2fs -b 4096
+ convert_test "$feature" "ext2 32k nodesize" 32768 mke2fs -b 4096
+ convert_test "$feature" "ext2 64k nodesize" 65536 mke2fs -b 4096
+done
diff --git a/tests/convert-tests/002-ext3-basic/test.sh b/tests/convert-tests/002-ext3-basic/test.sh
new file mode 100755
index 0000000..aeb111e
--- /dev/null
+++ b/tests/convert-tests/002-ext3-basic/test.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+source $TOP/tests/common
+source $TOP/tests/common.convert
+
+setup_root_helper
+prepare_test_dev 512M
+check_prereq btrfs-convert
+
+for feature in '' 'extref' 'skinny-metadata' 'no-holes'; do
+ convert_test "$feature" "ext3 4k nodesize" 4096 mke2fs -j -b 4096
+ convert_test "$feature" "ext3 8k nodesize" 8192 mke2fs -j -b 4096
+ convert_test "$feature" "ext3 16k nodesize" 16384 mke2fs -j -b 4096
+ convert_test "$feature" "ext3 32k nodesize" 32768 mke2fs -j -b 4096
+ convert_test "$feature" "ext3 64k nodesize" 65536 mke2fs -j -b 4096
+done
diff --git a/tests/convert-tests/003-ext4-basic/test.sh b/tests/convert-tests/003-ext4-basic/test.sh
new file mode 100755
index 0000000..531c81b
--- /dev/null
+++ b/tests/convert-tests/003-ext4-basic/test.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+source $TOP/tests/common
+source $TOP/tests/common.convert
+
+setup_root_helper
+prepare_test_dev 512M
+check_prereq btrfs-convert
+
+for feature in '' 'extref' 'skinny-metadata' 'no-holes'; do
+ convert_test "$feature" "ext4 4k nodesize" 4096 mke2fs -t ext4 -b 4096
+ convert_test "$feature" "ext4 8k nodesize" 8192 mke2fs -t ext4 -b 4096
+ convert_test "$feature" "ext4 16k nodesize" 16384 mke2fs -t ext4 -b 4096
+ convert_test "$feature" "ext4 32k nodesize" 32768 mke2fs -t ext4 -b 4096
+ convert_test "$feature" "ext4 64k nodesize" 65536 mke2fs -t ext4 -b 4096
+done
diff --git a/tests/convert-tests/004-ext2-backup-superblock-ranges/drdhdhdrd.e2image.raw.xz b/tests/convert-tests/004-ext2-backup-superblock-ranges/drdhdhdrd.e2image.raw.xz
new file mode 100644
index 0000000..73e2309
--- /dev/null
+++ b/tests/convert-tests/004-ext2-backup-superblock-ranges/drdhdhdrd.e2image.raw.xz
Binary files differ
diff --git a/tests/convert-tests/004-ext2-backup-superblock-ranges/drdhdhrh.e2image.raw.xz b/tests/convert-tests/004-ext2-backup-superblock-ranges/drdhdhrh.e2image.raw.xz
new file mode 100644
index 0000000..0d5442f
--- /dev/null
+++ b/tests/convert-tests/004-ext2-backup-superblock-ranges/drdhdhrh.e2image.raw.xz
Binary files differ
diff --git a/tests/convert-tests/004-ext2-backup-superblock-ranges/hrhdhdrd.e2image.raw.xz b/tests/convert-tests/004-ext2-backup-superblock-ranges/hrhdhdrd.e2image.raw.xz
new file mode 100644
index 0000000..a1a3042
--- /dev/null
+++ b/tests/convert-tests/004-ext2-backup-superblock-ranges/hrhdhdrd.e2image.raw.xz
Binary files differ
diff --git a/tests/convert-tests/004-ext2-backup-superblock-ranges/hrhdhrh.e2image.raw.xz b/tests/convert-tests/004-ext2-backup-superblock-ranges/hrhdhrh.e2image.raw.xz
new file mode 100644
index 0000000..ac0dbc6
--- /dev/null
+++ b/tests/convert-tests/004-ext2-backup-superblock-ranges/hrhdhrh.e2image.raw.xz
Binary files differ
diff --git a/tests/convert-tests/004-ext2-backup-superblock-ranges/test.sh b/tests/convert-tests/004-ext2-backup-superblock-ranges/test.sh
new file mode 100755
index 0000000..d85e4de
--- /dev/null
+++ b/tests/convert-tests/004-ext2-backup-superblock-ranges/test.sh
@@ -0,0 +1,43 @@
+#!/bin/bash
+
+# Specially created e2image dump to test backup superblock migration for
+# new convert.
+# These images will cause the following problems if convert doesn't handle
+# backup superblock migration well:
+# 1) Assert while building free space tree
+# 2) Error copying inodes
+# 3) Discontinuous file extents after convert
+# 4) Overlap file extents
+# 5) Unable to rollback
+
+source $TOP/tests/common
+
+check_prereq btrfs-convert
+check_prereq btrfs
+check_prereq btrfs-show-super
+check_global_prereq e2fsck
+check_global_prereq xzcat
+
+setup_root_helper
+prepare_test_dev 512M
+
+# override common function
+function check_image() {
+ TEST_DEV="$1"
+ run_check e2fsck -n -f $TEST_DEV
+ run_check $TOP/btrfs-convert $TEST_DEV
+ run_check $TOP/btrfs check $TEST_DEV
+ run_check $TOP/btrfs-show-super $TEST_DEV
+
+ run_check_mount_test_dev
+ run_check $SUDO_HELPER e2fsck -n -f $TEST_MNT/ext2_saved/image
+ run_check $SUDO_HELPER umount $TEST_MNT
+
+ run_check $TOP/btrfs check $TEST_DEV
+ run_check $TOP/btrfs-convert -r $TEST_DEV
+ run_check e2fsck -n -f $TEST_DEV
+
+ rm -f $TEST_DEV
+}
+
+check_all_images
diff --git a/tests/convert-tests/005-delete-all-rollback/test.sh b/tests/convert-tests/005-delete-all-rollback/test.sh
new file mode 100755
index 0000000..d498e5f
--- /dev/null
+++ b/tests/convert-tests/005-delete-all-rollback/test.sh
@@ -0,0 +1,61 @@
+#!/bin/bash
+# create a base image, convert to btrfs, remove all files, rollback the ext4 image
+# note: ext4 only
+
+source $TOP/tests/common
+source $TOP/tests/common.convert
+
+setup_root_helper
+prepare_test_dev 512M
+check_prereq btrfs-convert
+
+# simple wrapper for a convert test
+# $1: btrfs features, argument to -O
+# $2: message
+# $3: nodesize value
+# $4 + rest: command to create the ext2 image
+do_test() {
+ local features
+ local msg
+ local nodesize
+ local CHECKSUMTMP
+ local here
+
+ features="$1"
+ msg="$2"
+ nodesize="$3"
+ shift 3
+ convert_test_preamble "$features" "$msg" "$nodesize" "$@"
+ convert_test_prep_fs "$@"
+ populate_fs
+ CHECKSUMTMP=$(mktemp --tmpdir btrfs-progs-convert.XXXXXXXXXX)
+ convert_test_gen_checksums "$CHECKSUMTMP"
+
+ run_check_umount_test_dev
+
+ convert_test_do_convert "$features" "$nodesize"
+ convert_test_post_check "$CHECKSUMTMP"
+
+ run_check_mount_test_dev
+ here=$(pwd)
+ cd "$TEST_MNT" || _fail "cannot cd to TEST_MNT"
+ # ext2_saved/image must not be deleted
+ run_mayfail $SUDO_HELPER find "$TEST_MNT"/ -mindepth 1 -path '*ext2_saved' -prune -o -exec rm -vrf "{}" \;
+ cd "$here"
+ run_check $TOP/btrfs filesystem sync "$TEST_MNT"
+ run_check_umount_test_dev
+ convert_test_post_rollback
+ convert_test_post_check "$CHECKSUMTMP"
+
+ # mount again and verify checksums
+ convert_test_post_check "$CHECKSUMTMP"
+ rm "$CHECKSUMTMP"
+}
+
+for feature in '' 'extref' 'skinny-metadata' 'no-holes'; do
+ do_test "$feature" "ext4 4k nodesize" 4096 mke2fs -t ext4 -b 4096
+ do_test "$feature" "ext4 8k nodesize" 8192 mke2fs -t ext4 -b 4096
+ do_test "$feature" "ext4 16k nodesize" 16384 mke2fs -t ext4 -b 4096
+ do_test "$feature" "ext4 32k nodesize" 32768 mke2fs -t ext4 -b 4096
+ do_test "$feature" "ext4 64k nodesize" 65536 mke2fs -t ext4 -b 4096
+done
diff --git a/tests/fsck-tests.sh b/tests/fsck-tests.sh
index 2aab4ff..d1cd732 100755
--- a/tests/fsck-tests.sh
+++ b/tests/fsck-tests.sh
@@ -1,25 +1,21 @@
#!/bin/bash
#
# loop through all of our bad images and make sure fsck repairs them properly
-#
-# It's GPL, same as everything else in this tree.
-#
-unset TOP
-unset LANG
LANG=C
SCRIPT_DIR=$(dirname $(readlink -f $0))
TOP=$(readlink -f $SCRIPT_DIR/../)
TEST_DEV=${TEST_DEV:-}
RESULTS="$TOP/tests/fsck-tests-results.txt"
+IMAGE="$TOP/tests/test.img"
source $TOP/tests/common
-# Allow child test to use $TOP and $RESULTS
export TOP
export RESULTS
-# For custom script needs to verfiy recovery
export LANG
+export IMAGE
+export TEST_DEV
rm -f $RESULTS
diff --git a/tests/fsck-tests/020-extent-ref-cases/keyed_block_ref.img b/tests/fsck-tests/020-extent-ref-cases/keyed_block_ref.img
new file mode 100644
index 0000000..289d37b
--- /dev/null
+++ b/tests/fsck-tests/020-extent-ref-cases/keyed_block_ref.img
Binary files differ
diff --git a/tests/fsck-tests/020-extent-ref-cases/keyed_data_ref.img b/tests/fsck-tests/020-extent-ref-cases/keyed_data_ref.img
new file mode 100644
index 0000000..2ac0ae5
--- /dev/null
+++ b/tests/fsck-tests/020-extent-ref-cases/keyed_data_ref.img
Binary files differ
diff --git a/tests/fsck-tests/020-extent-ref-cases/no_inline_ref.img b/tests/fsck-tests/020-extent-ref-cases/no_inline_ref.img
new file mode 100644
index 0000000..b05ae73
--- /dev/null
+++ b/tests/fsck-tests/020-extent-ref-cases/no_inline_ref.img
Binary files differ
diff --git a/tests/fsck-tests/020-extent-ref-cases/no_skinny_ref.img b/tests/fsck-tests/020-extent-ref-cases/no_skinny_ref.img
new file mode 100644
index 0000000..900b65c
--- /dev/null
+++ b/tests/fsck-tests/020-extent-ref-cases/no_skinny_ref.img
Binary files differ
diff --git a/tests/fsck-tests/020-extent-ref-cases/shared_block_ref.img b/tests/fsck-tests/020-extent-ref-cases/shared_block_ref.img
new file mode 100644
index 0000000..8d7b50f
--- /dev/null
+++ b/tests/fsck-tests/020-extent-ref-cases/shared_block_ref.img
Binary files differ
diff --git a/tests/fsck-tests/020-extent-ref-cases/shared_data_ref.img b/tests/fsck-tests/020-extent-ref-cases/shared_data_ref.img
new file mode 100644
index 0000000..aa2dafa
--- /dev/null
+++ b/tests/fsck-tests/020-extent-ref-cases/shared_data_ref.img
Binary files differ
diff --git a/tests/fsck-tests/020-extent-ref-cases/test.sh b/tests/fsck-tests/020-extent-ref-cases/test.sh
new file mode 100755
index 0000000..c2b6a00
--- /dev/null
+++ b/tests/fsck-tests/020-extent-ref-cases/test.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+# In order to confirm that btrfsck supports to check a variety of refs, add the
+# following cases:
+#
+# * keyed_block_ref
+# * keyed_data_ref
+# * shared_block_ref
+# * shared_data_ref
+# * no_inline_ref (a extent item without inline ref)
+# * no_skinny_ref
+
+source $TOP/tests/common
+
+check_prereq btrfs
+
+for img in *.img
+do
+ image=$(extract_image $img)
+ run_check_stdout $TOP/btrfs check "$image" 2>&1 |
+ grep -q "Errors found in extent allocation tree or chunk allocation" &&
+ _fail "unexpected error occurred when checking $img"
+ rm -f "$image"
+done
diff --git a/tests/fuzz-tests.sh b/tests/fuzz-tests.sh
index 204dce2..29691ca 100755
--- a/tests/fuzz-tests.sh
+++ b/tests/fuzz-tests.sh
@@ -2,8 +2,6 @@
#
# misc tests on fuzzed or crafted images
-unset TOP
-unset LANG
LANG=C
SCRIPT_DIR=$(dirname $(readlink -f $0))
TOP=$(readlink -f $SCRIPT_DIR/../)
@@ -17,6 +15,7 @@ export TOP
export RESULTS
export LANG
export IMAGE
+export TEST_DEV
rm -f $RESULTS
diff --git a/tests/fuzz-tests/images/superblock-stripsize-bogus.raw.txt b/tests/fuzz-tests/images/superblock-stripsize-bogus.raw.txt
new file mode 100644
index 0000000..80e073f
--- /dev/null
+++ b/tests/fuzz-tests/images/superblock-stripsize-bogus.raw.txt
@@ -0,0 +1,32 @@
+[ 125.415910] BTRFS info (device loop0): disk space caching is enabled
+[ 125.550479] ------------[ cut here ]------------
+[ 125.551145] WARNING: CPU: 6 PID: 1496 at fs/btrfs/locking.c:251 btrfs_tree_lock+0x22e/0x250
+[ 125.552292] Modules linked in:
+[ 125.552602] CPU: 6 PID: 1496 Comm: btrfs.exe Tainted: G W 4.6.0-rc5 #130
+[ 125.553138] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.8.2-20150714_191134- 04/01/2014
+[ 125.553775] 0000000000000286 000000009b4bdd50 ffff88006a7478e0 ffffffff8157e563
+[ 125.554299] 0000000000000000 0000000000000000 ffff88006a747920 ffffffff810a74ab
+[ 125.554825] 000000fb8146c531 ffff88006bfec460 ffff88006bc63000 0000000000000000
+[ 125.555373] Call Trace:
+[ 125.555545] [<ffffffff8157e563>] dump_stack+0x85/0xc2
+[ 125.555892] [<ffffffff810a74ab>] __warn+0xcb/0xf0
+[ 125.556226] [<ffffffff810a75dd>] warn_slowpath_null+0x1d/0x20
+[ 125.556654] [<ffffffff814871ee>] btrfs_tree_lock+0x22e/0x250
+[ 125.557041] [<ffffffff81423831>] btrfs_init_new_buffer+0x81/0x160
+[ 125.557458] [<ffffffff8143472a>] btrfs_alloc_tree_block+0x22a/0x430
+[ 125.557883] [<ffffffff8141ae61>] __btrfs_cow_block+0x141/0x590
+[ 125.558279] [<ffffffff8141b44f>] btrfs_cow_block+0x11f/0x1f0
+[ 125.558666] [<ffffffff8141f09e>] btrfs_search_slot+0x1fe/0xa30
+[ 125.559063] [<ffffffff81247c9d>] ? kmem_cache_alloc+0xfd/0x240
+[ 125.559482] [<ffffffff8143b1f0>] btrfs_del_inode_ref+0x80/0x380
+[ 125.559884] [<ffffffff8148e11a>] ? btrfs_del_inode_ref_in_log+0x8a/0x160
+[ 125.560340] [<ffffffff8148e14d>] btrfs_del_inode_ref_in_log+0xbd/0x160
+[ 125.560776] [<ffffffff814507f7>] __btrfs_unlink_inode+0x1d7/0x470
+[ 125.561188] [<ffffffff814567a7>] btrfs_rename2+0x327/0x790
+[ 125.561568] [<ffffffff8127b398>] vfs_rename+0x4d8/0x840
+[ 125.561928] [<ffffffff81281b21>] SyS_rename+0x371/0x390
+[ 125.562289] [<ffffffff819cfd3c>] entry_SYSCALL_64_fastpath+0x1f/0xbd
+[ 125.562743] ---[ end trace 3b751f511705fb90 ]---
+
+---------------------------------------------------------------------------
+Fixed by patch:
diff --git a/tests/fuzz-tests/images/superblock-stripsize-bogus.raw.xz b/tests/fuzz-tests/images/superblock-stripsize-bogus.raw.xz
new file mode 100644
index 0000000..f8b3bf5
--- /dev/null
+++ b/tests/fuzz-tests/images/superblock-stripsize-bogus.raw.xz
Binary files differ
diff --git a/tests/fuzz-tests/images/superblock-total-bytes-0.raw.txt b/tests/fuzz-tests/images/superblock-total-bytes-0.raw.txt
new file mode 100644
index 0000000..d5e1f93
--- /dev/null
+++ b/tests/fuzz-tests/images/superblock-total-bytes-0.raw.txt
@@ -0,0 +1,50 @@
+[342246.846031] BTRFS info (device loop0): disk space caching is enabled
+[342246.862115] ------------[ cut here ]------------
+[342246.862500] kernel BUG at fs/btrfs/inode.c:978!
+[342246.862861] invalid opcode: 0000 [#1] SMP
+[342246.863176] Modules linked in:
+[342246.863410] CPU: 2 PID: 14504 Comm: btrfs.exe Tainted: G W 4.6.0-rc5 #130
+[342246.864010] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.8.2-20150714_191134- 04/01/2014
+[342246.864674] task: ffff88006fdf0000 ti: ffff8800702e0000 task.ti: ffff8800702e0000
+[342246.865186] RIP: 0010:[<ffffffff8144e9c7>] [<ffffffff8144e9c7>] cow_file_range+0x3f7/0x440
+[342246.865770] RSP: 0018:ffff8800702e39e0 EFLAGS: 00010206
+[342246.866157] RAX: ffff88006bb23000 RBX: 0000000000000001 RCX: 0000000000010000
+[342246.866687] RDX: 0000000000000000 RSI: 0000000000001000 RDI: 0000000000010000
+[342246.867191] RBP: ffff8800702e3a70 R08: 0000000000000000 R09: 0000000000000000
+[342246.867682] R10: 000000000000ffff R11: 0000000000010000 R12: ffff8800702e3bc0
+[342246.868170] R13: ffff8800702e3b3c R14: 0000000000000000 R15: ffff880075369c10
+[342246.868660] FS: 00007f96f5a38700(0000) GS:ffff88007ca00000(0000) knlGS:0000000000000000
+[342246.869212] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
+[342246.869642] CR2: 000000000060f4bf CR3: 000000006fc9f000 CR4: 00000000000006e0
+[342246.870146] Stack:
+[342246.870295] 0000000000000000 0000000000000001 000000000000ffff ffffea00010c08c0
+[342246.870838] ffff8800753698e8 0000000000010000 ffff88006fe0f000 000000000000ffff
+[342246.871397] 000000000000ffff ffffffff814683e5 ffff8800753698c8 ffff8800753698e8
+[342246.871944] Call Trace:
+[342246.872124] [<ffffffff814683e5>] ? test_range_bit+0xe5/0x130
+[342246.872522] [<ffffffff8144f906>] run_delalloc_range+0x396/0x3d0
+[342246.872975] [<ffffffff8146873f>] writepage_delalloc.isra.42+0x10f/0x170
+[342246.873437] [<ffffffff8146a674>] __extent_writepage+0xf4/0x370
+[342246.873848] [<ffffffff8146abf4>] extent_write_cache_pages.isra.39.constprop.57+0x304/0x3f0
+[342246.874419] [<ffffffff8146beec>] extent_writepages+0x5c/0x90
+[342246.874818] [<ffffffff8144c870>] ? btrfs_real_readdir+0x5f0/0x5f0
+[342246.875245] [<ffffffff814498f8>] btrfs_writepages+0x28/0x30
+[342246.875641] [<ffffffff811ebc61>] do_writepages+0x21/0x30
+[342246.876031] [<ffffffff811dc1a6>] __filemap_fdatawrite_range+0xc6/0x100
+[342246.876487] [<ffffffff811dc2b3>] filemap_fdatawrite_range+0x13/0x20
+[342246.876949] [<ffffffff8145eae0>] btrfs_fdatawrite_range+0x20/0x50
+[342246.877375] [<ffffffff8145eb29>] start_ordered_ops+0x19/0x30
+[342246.877774] [<ffffffff8145ebc2>] btrfs_sync_file+0x82/0x3f0
+[342246.878166] [<ffffffff810fb717>] ? update_fast_ctr+0x17/0x30
+[342246.878564] [<ffffffff812a848b>] vfs_fsync_range+0x4b/0xb0
+[342246.878987] [<ffffffff8128fce6>] ? __fget_light+0x66/0x90
+[342246.879368] [<ffffffff812a854d>] do_fsync+0x3d/0x70
+[342246.879708] [<ffffffff812a8823>] SyS_fdatasync+0x13/0x20
+[342246.880099] [<ffffffff819cfd3c>] entry_SYSCALL_64_fastpath+0x1f/0xbd
+[342246.880554] Code: 03 00 00 48 c7 c7 00 b3 c9 81 c6 05 54 b6 b1 00 01 e8 0e 8c c5 ff e9 e5 fe ff ff 49 8b 57 40 e9 c0 fe ff ff bb f4 ff ff ff eb a1 <0f> 0b 48 8b 55 80 41 b9 0f 00 00 00 41 b8 68 00 00 00 31 c9 31
+[342246.882394] RIP [<ffffffff8144e9c7>] cow_file_range+0x3f7/0x440
+[342246.882810] RSP <ffff8800702e39e0>
+[342246.883076] ---[ end trace 094193b6df6e45e7 ]---
+
+--------------------------------------------------------
+Fixed by patch:
diff --git a/tests/fuzz-tests/images/superblock-total-bytes-0.raw.xz b/tests/fuzz-tests/images/superblock-total-bytes-0.raw.xz
new file mode 100644
index 0000000..4b25020
--- /dev/null
+++ b/tests/fuzz-tests/images/superblock-total-bytes-0.raw.xz
Binary files differ
diff --git a/tests/fuzz-tests/images/sys-chunk-stripe-len-bogus.raw.txt b/tests/fuzz-tests/images/sys-chunk-stripe-len-bogus.raw.txt
new file mode 100644
index 0000000..d3dcb0a
--- /dev/null
+++ b/tests/fuzz-tests/images/sys-chunk-stripe-len-bogus.raw.txt
@@ -0,0 +1,54 @@
+[ 135.166891] BTRFS info (device loop0): disk space caching is enabled
+[ 135.169199] divide error: 0000 [#1] SMP
+[ 135.169581] Modules linked in:
+[ 135.169819] CPU: 2 PID: 1512 Comm: btrfs.exe Tainted: G W 4.6.0-rc5 #130
+[ 135.170285] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.8.2-20150714_191134- 04/01/2014
+[ 135.170958] task: ffff880074925180 ti: ffff880077fa4000 task.ti: ffff880077fa4000
+[ 135.171583] RIP: 0010:[<ffffffff81475ba0>] [<ffffffff81475ba0>] __btrfs_map_block+0xc0/0x11b0
+[ 135.172096] RSP: 0000:ffff880077fa77b0 EFLAGS: 00010206
+[ 135.172374] RAX: 0000000000020000 RBX: 0000000000020000 RCX: 0000000000000000
+[ 135.172754] RDX: 0000000000000000 RSI: 0000000000400000 RDI: ffff880076258270
+[ 135.173143] RBP: ffff880077fa7898 R08: 0000000000400000 R09: 0000000000000000
+[ 135.173523] R10: 0000000000000001 R11: 0000000000000000 R12: 0000000000020000
+[ 135.173916] R13: ffff880076258270 R14: ffff880077fa78e0 R15: ffff88006bb3b000
+[ 135.174290] FS: 00007fd8267dc700(0000) GS:ffff88007ca00000(0000) knlGS:0000000000000000
+[ 135.174718] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
+[ 135.175019] CR2: 00007ffe9c378df7 CR3: 0000000078788000 CR4: 00000000000006e0
+[ 135.175392] Stack:
+[ 135.175503] ffff88007cbe2c40 0000000000000000 ffff88007cbe2c50 ffff880074925180
+[ 135.175924] ffff880074926560 ffff880074925180 0000000200000000 0000000000000000
+[ 135.176340] ffffffffffffffff 0007ffffffffffff ffffffff8143eb18 0240004000000000
+[ 135.176778] Call Trace:
+[ 135.176913] [<ffffffff8143eb18>] ? btrfs_bio_wq_end_io+0x28/0x70
+[ 135.177234] [<ffffffff81477218>] btrfs_map_bio+0x88/0x350
+[ 135.177522] [<ffffffff8143eb18>] ? btrfs_bio_wq_end_io+0x28/0x70
+[ 135.177960] [<ffffffff8143ed9d>] btree_submit_bio_hook+0x6d/0x110
+[ 135.178410] [<ffffffff81464d1d>] submit_one_bio+0x6d/0xa0
+[ 135.178814] [<ffffffff8146d6f1>] read_extent_buffer_pages+0x1c1/0x350
+[ 135.179276] [<ffffffff8143cd60>] ? free_root_pointers+0x70/0x70
+[ 135.179708] [<ffffffff8143e12c>] btree_read_extent_buffer_pages.constprop.55+0xac/0x110
+[ 135.180261] [<ffffffff8143f036>] read_tree_block+0x36/0x60
+[ 135.180647] [<ffffffff81443b52>] open_ctree+0x17a2/0x2900
+[ 135.181027] [<ffffffff81417225>] btrfs_mount+0xd05/0xe60
+[ 135.181400] [<ffffffff819cd15a>] ? __mutex_unlock_slowpath+0xfa/0x1c0
+[ 135.181850] [<ffffffff810fd3e4>] ? lockdep_init_map+0x64/0x710
+[ 135.182241] [<ffffffff81272918>] mount_fs+0x38/0x170
+[ 135.182609] [<ffffffff81292b7b>] vfs_kern_mount+0x6b/0x150
+[ 135.182998] [<ffffffff814166e6>] btrfs_mount+0x1c6/0xe60
+[ 135.183372] [<ffffffff819cd15a>] ? __mutex_unlock_slowpath+0xfa/0x1c0
+[ 135.183825] [<ffffffff810fd3e4>] ? lockdep_init_map+0x64/0x710
+[ 135.184233] [<ffffffff81272918>] mount_fs+0x38/0x170
+[ 135.184583] [<ffffffff81292b7b>] vfs_kern_mount+0x6b/0x150
+[ 135.184971] [<ffffffff812958c6>] do_mount+0x256/0xeb0
+[ 135.185318] [<ffffffff8124bb33>] ? __kmalloc_track_caller+0x113/0x290
+[ 135.185759] [<ffffffff812b0b63>] ? block_ioctl+0x43/0x50
+[ 135.186124] [<ffffffff811ff023>] ? memdup_user+0x53/0x80
+[ 135.186488] [<ffffffff81296865>] SyS_mount+0x95/0xe0
+[ 135.186877] [<ffffffff819cfd3c>] entry_SYSCALL_64_fastpath+0x1f/0xbd
+[ 135.187308] Code: 8b 70 20 4c 8d 04 31 4c 39 c3 0f 87 2f 0b 00 00 48 8b 45 a8 49 89 dc 31 d2 49 29 cc 48 8b 40 70 48 63 48 10 48 89 45 a0 4c 89 e0 <48> f7 f1 49 89 cf 48 89 45 b8 48 0f af c1 49 39 c4 0f 82 c3 0a
+[ 135.189097] RIP [<ffffffff81475ba0>] __btrfs_map_block+0xc0/0x11b0
+[ 135.189527] RSP <ffff880077fa77b0>
+[ 135.189819] ---[ end trace ea21fae64670799a ]---
+
+---------------------------------------------------------------------------
+Fixed by patch:
diff --git a/tests/fuzz-tests/images/sys-chunk-stripe-len-bogus.raw.xz b/tests/fuzz-tests/images/sys-chunk-stripe-len-bogus.raw.xz
new file mode 100644
index 0000000..57d2a72
--- /dev/null
+++ b/tests/fuzz-tests/images/sys-chunk-stripe-len-bogus.raw.xz
Binary files differ
diff --git a/tests/fuzz-tests/images/sys-chunk-type-bogus.raw.txt b/tests/fuzz-tests/images/sys-chunk-type-bogus.raw.txt
new file mode 100644
index 0000000..2559924
--- /dev/null
+++ b/tests/fuzz-tests/images/sys-chunk-type-bogus.raw.txt
@@ -0,0 +1,55 @@
+[ 145.676440] BTRFS error (device loop0): bad tree block start 0 131072
+[ 145.677032] ------------[ cut here ]------------
+[ 145.677307] kernel BUG at fs/btrfs/raid56.c:2142!
+[ 145.677627] invalid opcode: 0000 [#1] SMP
+[ 145.677955] Modules linked in:
+[ 145.678182] CPU: 3 PID: 1538 Comm: btrfs.exe Tainted: G W 4.6.0-rc5 #130
+[ 145.678734] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.8.2-20150714_191134- 04/01/2014
+[ 145.679402] task: ffff88006c830000 ti: ffff88006fc74000 task.ti: ffff88006fc74000
+[ 145.679919] RIP: 0010:[<ffffffff814c5794>] [<ffffffff814c5794>] raid56_parity_recover+0xc4/0x160
+[ 145.680514] RSP: 0018:ffff88006fc77868 EFLAGS: 00010286
+[ 145.680865] RAX: ffff88006f725280 RBX: ffff880070ba0a68 RCX: 0000000000020000
+[ 145.681373] RDX: 0000000000000100 RSI: 00000000ffffffff RDI: ffffffff831229e8
+[ 145.681866] RBP: ffff88006fc77898 R08: 0000000000010000 R09: ffff8800768ff400
+[ 145.682380] R10: ffff88007c003180 R11: 0000000000030000 R12: ffff88006f725280
+[ 145.682870] R13: ffff88007b449000 R14: 0000000000000001 R15: ffff8800768ff400
+[ 145.683363] FS: 00007f68b95a8700(0000) GS:ffff88007cc00000(0000) knlGS:0000000000000000
+[ 145.683941] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
+[ 145.684340] CR2: 00007fff0d130f98 CR3: 000000006bfd7000 CR4: 00000000000006e0
+[ 145.684832] Stack:
+[ 145.684977] 00000002e6816dd1 ffff880070ba0a68 ffff88007b449000 0000000000000001
+[ 145.685541] 0000000000020000 0000000000000002 ffff88006fc77920 ffffffff814773cd
+[ 145.686082] ffff880000000001 0000000002400040 ffff88006fc778f8 0000000081247c9d
+[ 145.686654] Call Trace:
+[ 145.686831] [<ffffffff814773cd>] btrfs_map_bio+0x23d/0x350
+[ 145.687217] [<ffffffff8143ed9d>] btree_submit_bio_hook+0x6d/0x110
+[ 145.687649] [<ffffffff81464d1d>] submit_one_bio+0x6d/0xa0
+[ 145.688028] [<ffffffff8146d6f1>] read_extent_buffer_pages+0x1c1/0x350
+[ 145.688501] [<ffffffff8143cd60>] ? free_root_pointers+0x70/0x70
+[ 145.688916] [<ffffffff8143e12c>] btree_read_extent_buffer_pages.constprop.55+0xac/0x110
+[ 145.689474] [<ffffffff8143f036>] read_tree_block+0x36/0x60
+[ 145.689861] [<ffffffff81443b52>] open_ctree+0x17a2/0x2900
+[ 145.690242] [<ffffffff81417225>] btrfs_mount+0xd05/0xe60
+[ 145.690623] [<ffffffff819cd15a>] ? __mutex_unlock_slowpath+0xfa/0x1c0
+[ 145.691064] [<ffffffff810fd3e4>] ? lockdep_init_map+0x64/0x710
+[ 145.691510] [<ffffffff81272918>] mount_fs+0x38/0x170
+[ 145.691852] [<ffffffff81292b7b>] vfs_kern_mount+0x6b/0x150
+[ 145.692227] [<ffffffff814166e6>] btrfs_mount+0x1c6/0xe60
+[ 145.692594] [<ffffffff819cd15a>] ? __mutex_unlock_slowpath+0xfa/0x1c0
+[ 145.693032] [<ffffffff810fd3e4>] ? lockdep_init_map+0x64/0x710
+[ 145.693453] [<ffffffff81272918>] mount_fs+0x38/0x170
+[ 145.693793] [<ffffffff81292b7b>] vfs_kern_mount+0x6b/0x150
+[ 145.694168] [<ffffffff812958c6>] do_mount+0x256/0xeb0
+[ 145.694537] [<ffffffff8124bb33>] ? __kmalloc_track_caller+0x113/0x290
+[ 145.694974] [<ffffffff812b0b63>] ? block_ioctl+0x43/0x50
+[ 145.695338] [<ffffffff811ff023>] ? memdup_user+0x53/0x80
+[ 145.695703] [<ffffffff81296865>] SyS_mount+0x95/0xe0
+[ 145.696046] [<ffffffff819cfd3c>] entry_SYSCALL_64_fastpath+0x1f/0xbd
+[ 145.696480] Code: 1f 48 8b 78 58 31 c0 48 8b 14 c7 48 39 d1 72 08 4c 01 c2 48 39 d1 72 15 48 83 c0 01 39 c6 7f e7 41 c7 87 3c 01 00 00 ff ff ff ff <0f> 0b 45 85 f6 41 89 87 3c 01 00 00 75 35 4c 89 e7 e8 e6 02 fb
+[ 145.698326] RIP [<ffffffff814c5794>] raid56_parity_recover+0xc4/0x160
+[ 145.698771] RSP <ffff88006fc77868>
+[ 145.699047] ---[ end trace 22f39f01df276367 ]---
+
+-----------------------------------------------------
+Fixed by patch:
+
diff --git a/tests/fuzz-tests/images/sys-chunk-type-bogus.raw.xz b/tests/fuzz-tests/images/sys-chunk-type-bogus.raw.xz
new file mode 100644
index 0000000..ef971ca
--- /dev/null
+++ b/tests/fuzz-tests/images/sys-chunk-type-bogus.raw.xz
Binary files differ
diff --git a/tests/misc-tests.sh b/tests/misc-tests.sh
index 2a7f57c..eefe8a8 100755
--- a/tests/misc-tests.sh
+++ b/tests/misc-tests.sh
@@ -2,8 +2,6 @@
#
# Misc tests
-unset TOP
-unset LANG
LANG=C
SCRIPT_DIR=$(dirname $(readlink -f $0))
TOP=$(readlink -f $SCRIPT_DIR/../)
@@ -13,12 +11,10 @@ IMAGE="$TOP/tests/test.img"
source $TOP/tests/common
-# Allow child test to use $TOP and $RESULTS
export TOP
export RESULTS
-# For custom script needs to verfiy recovery
export LANG
-# For tests that only use a loop device
+export TEST_DEV
export IMAGE
rm -f $RESULTS
diff --git a/tests/misc-tests/007-subvolume-sync/test.sh b/tests/misc-tests/007-subvolume-sync/test.sh
index a745fb5..243bb8c 100755
--- a/tests/misc-tests/007-subvolume-sync/test.sh
+++ b/tests/misc-tests/007-subvolume-sync/test.sh
@@ -1,7 +1,7 @@
#!/bin/bash
# test btrfs subvolume run normally with more than one subvolume
#
-# - btrfs subvolume must not loop indefinetelly
+# - btrfs subvolume must not loop indefinitely
# - btrfs subvolume return 0 in normal case
source $TOP/tests/common
@@ -16,7 +16,7 @@ run_check $SUDO_HELPER $TOP/mkfs.btrfs -f "$TEST_DEV"
run_check_mount_test_dev
# to check following thing in both 1 and multiple subvolume case:
-# 1: is subvolume sync loop indefinetelly
+# 1: is subvolume sync loop indefinitely
# 2: is return value right
#
run_check $SUDO_HELPER $TOP/btrfs subvolume create "$TEST_MNT"/mysubvol1
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 056584e..92c896f 100755
--- a/tests/misc-tests/009-subvolume-sync-must-wait/test.sh
+++ b/tests/misc-tests/009-subvolume-sync-must-wait/test.sh
@@ -1,6 +1,6 @@
#!/bin/bash
#
-# Verify that subovolume sync waits until the subvolume is cleaned
+# Verify that subvolume sync waits until the subvolume is cleaned
source $TOP/tests/common
diff --git a/tests/misc-tests/013-subvolume-sync-crash/test.sh b/tests/misc-tests/013-subvolume-sync-crash/test.sh
index c795526..4cb1b4e 100755
--- a/tests/misc-tests/013-subvolume-sync-crash/test.sh
+++ b/tests/misc-tests/013-subvolume-sync-crash/test.sh
@@ -1,6 +1,6 @@
#!/bin/bash
#
-# Verify that subovolume sync waits until the subvolume is cleaned and does not
+# Verify that subvolume sync waits until the subvolume is cleaned and does not
# crash at the end
source $TOP/tests/common
diff --git a/tests/mkfs-tests.sh b/tests/mkfs-tests.sh
index c0635ad..1afc028 100755
--- a/tests/mkfs-tests.sh
+++ b/tests/mkfs-tests.sh
@@ -2,8 +2,6 @@
#
# mkfs.btrfs tests
-unset TOP
-unset LANG
LANG=C
SCRIPT_DIR=$(dirname $(readlink -f $0))
TOP=$(readlink -f $SCRIPT_DIR/../)
@@ -13,13 +11,11 @@ IMAGE="$TOP/tests/test.img"
source $TOP/tests/common
-# Allow child test to use $TOP and $RESULTS
export TOP
export RESULTS
-# For custom script needs to verfiy recovery
export LANG
-# For tests that only use a loop device
export IMAGE
+export TEST_DEV
rm -f $RESULTS
diff --git a/tests/mkfs-tests/008-secorsize-nodesize-combination/test.sh b/tests/mkfs-tests/008-secorsize-nodesize-combination/test.sh
index 79cc2b2..151e7b7 100755
--- a/tests/mkfs-tests/008-secorsize-nodesize-combination/test.sh
+++ b/tests/mkfs-tests/008-secorsize-nodesize-combination/test.sh
@@ -34,7 +34,7 @@ do_test 8191 8191 && _fail
# Invalid: Aligned sectorsize with unaligned nodesize
do_test 4k 16385 && _fail
-# Invalid: Ungliend sectorsize with aligned nodesize
+# Invalid: Unaligned sectorsize with aligned nodesize
do_test 8191 16k && _fail
# Valid: Aligned sectorsize and nodesize
diff --git a/tests/test-console.sh b/tests/test-console.sh
new file mode 100755
index 0000000..cc1cdf3
--- /dev/null
+++ b/tests/test-console.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+# a shell with test environment set up, logged commands and output
+
+LANG=C
+SCRIPT_DIR=$(dirname $(readlink -f $0))
+TOP=$(readlink -f $SCRIPT_DIR/../)
+TEST_DEV=${TEST_DEV:-}
+RESULTS="$TOP/tests/test-console.txt"
+IMAGE="$TOP/tests/test.img"
+
+source common
+source common.convert
+
+setup_root_helper
+
+echo "Eval loop in test environment (log: $RESULTS)"
+echo -e " ---------------------\nStarting session, `date`" >> "$RESULTS"
+echo -n "`pwd`> "
+while read x; do
+ echo "COMMAND: $x" >> "$RESULTS"
+ { eval $x; } 2>&1 | tee -a "$RESULTS"
+ echo -n "`pwd`> "
+done
diff --git a/ulist.c b/ulist.c
index 60fdc09..c5eca45 100644
--- a/ulist.c
+++ b/ulist.c
@@ -30,7 +30,7 @@
* }
* ulist_free(ulist);
*
- * This assumes the graph nodes are adressable by u64. This stems from the
+ * This assumes the graph nodes are addressable by u64. This stems from the
* usage for tree enumeration in btrfs, where the logical addresses are
* 64 bit.
*
diff --git a/utils.c b/utils.c
index 7e45702..578fdb0 100644
--- a/utils.c
+++ b/utils.c
@@ -58,6 +58,9 @@ static int btrfs_scan_done = 0;
static char argv0_buf[ARGV0_BUF_SIZE] = "btrfs";
+static int rand_seed_initlized = 0;
+static unsigned short rand_seed[3];
+
const char *get_argv0_buf(void)
{
return argv0_buf;
@@ -179,9 +182,831 @@ int test_uuid_unique(char *fs_uuid)
}
/*
+ * Reserve space from free_tree.
+ * The algorithm is very simple, find the first cache_extent with enough space
+ * and allocate from its beginning.
+ */
+static int reserve_free_space(struct cache_tree *free_tree, u64 len,
+ u64 *ret_start)
+{
+ struct cache_extent *cache;
+ int found = 0;
+
+ BUG_ON(!ret_start);
+ cache = first_cache_extent(free_tree);
+ while (cache) {
+ if (cache->size > len) {
+ found = 1;
+ *ret_start = cache->start;
+
+ cache->size -= len;
+ if (cache->size == 0) {
+ remove_cache_extent(free_tree, cache);
+ free(cache);
+ } else {
+ cache->start += len;
+ }
+ break;
+ }
+ cache = next_cache_extent(cache);
+ }
+ if (!found)
+ return -ENOSPC;
+ return 0;
+}
+
+static inline int write_temp_super(int fd, struct btrfs_super_block *sb,
+ u64 sb_bytenr)
+{
+ u32 crc = ~(u32)0;
+ int ret;
+
+ crc = btrfs_csum_data(NULL, (char *)sb + BTRFS_CSUM_SIZE, crc,
+ BTRFS_SUPER_INFO_SIZE - BTRFS_CSUM_SIZE);
+ btrfs_csum_final(crc, (char *)&sb->csum[0]);
+ ret = pwrite(fd, sb, BTRFS_SUPER_INFO_SIZE, sb_bytenr);
+ if (ret < BTRFS_SUPER_INFO_SIZE)
+ ret = (ret < 0 ? -errno : -EIO);
+ else
+ ret = 0;
+ return ret;
+}
+
+/*
+ * Setup temporary superblock at cfg->super_bynter
+ * Needed info are extracted from cfg, and root_bytenr, chunk_bytenr
+ *
+ * For now sys chunk array will be empty and dev_item is empty too.
+ * They will be re-initialized at temp chunk tree setup.
+ */
+static int setup_temp_super(int fd, struct btrfs_mkfs_config *cfg,
+ u64 root_bytenr, u64 chunk_bytenr)
+{
+ unsigned char chunk_uuid[BTRFS_UUID_SIZE];
+ char super_buf[BTRFS_SUPER_INFO_SIZE];
+ struct btrfs_super_block *super = (struct btrfs_super_block *)super_buf;
+ int ret;
+
+ /*
+ * We rely on cfg->chunk_uuid and cfg->fs_uuid to pass uuid
+ * for other functions.
+ * Caller must allocate space for them
+ */
+ BUG_ON(!cfg->chunk_uuid || !cfg->fs_uuid);
+ memset(super_buf, 0, BTRFS_SUPER_INFO_SIZE);
+ cfg->num_bytes = round_down(cfg->num_bytes, cfg->sectorsize);
+
+ if (cfg->fs_uuid && *cfg->fs_uuid) {
+ if (uuid_parse(cfg->fs_uuid, super->fsid) != 0) {
+ error("cound not parse UUID: %s", cfg->fs_uuid);
+ ret = -EINVAL;
+ goto out;
+ }
+ if (!test_uuid_unique(cfg->fs_uuid)) {
+ error("non-unique UUID: %s", cfg->fs_uuid);
+ ret = -EINVAL;
+ goto out;
+ }
+ } else {
+ uuid_generate(super->fsid);
+ uuid_unparse(super->fsid, cfg->fs_uuid);
+ }
+ uuid_generate(chunk_uuid);
+ uuid_unparse(chunk_uuid, cfg->chunk_uuid);
+
+ btrfs_set_super_bytenr(super, cfg->super_bytenr);
+ btrfs_set_super_num_devices(super, 1);
+ btrfs_set_super_magic(super, BTRFS_MAGIC);
+ btrfs_set_super_generation(super, 1);
+ btrfs_set_super_root(super, root_bytenr);
+ btrfs_set_super_chunk_root(super, chunk_bytenr);
+ btrfs_set_super_total_bytes(super, cfg->num_bytes);
+ /*
+ * Temporary filesystem will only have 6 tree roots:
+ * chunk tree, root tree, extent_tree, device tree, fs tree
+ * and csum tree.
+ */
+ btrfs_set_super_bytes_used(super, 6 * cfg->nodesize);
+ btrfs_set_super_sectorsize(super, cfg->sectorsize);
+ btrfs_set_super_leafsize(super, cfg->nodesize);
+ btrfs_set_super_nodesize(super, cfg->nodesize);
+ btrfs_set_super_stripesize(super, cfg->stripesize);
+ btrfs_set_super_csum_type(super, BTRFS_CSUM_TYPE_CRC32);
+ btrfs_set_super_chunk_root(super, chunk_bytenr);
+ btrfs_set_super_cache_generation(super, -1);
+ btrfs_set_super_incompat_flags(super, cfg->features);
+ if (cfg->label)
+ __strncpy_null(super->label, cfg->label, BTRFS_LABEL_SIZE - 1);
+
+ /* Sys chunk array will be re-initialized at chunk tree init time */
+ super->sys_chunk_array_size = 0;
+
+ ret = write_temp_super(fd, super, cfg->super_bytenr);
+out:
+ return ret;
+}
+
+/*
+ * Setup an extent buffer for tree block.
+ */
+static int setup_temp_extent_buffer(struct extent_buffer *buf,
+ struct btrfs_mkfs_config *cfg,
+ u64 bytenr, u64 owner)
+{
+ unsigned char fsid[BTRFS_FSID_SIZE];
+ unsigned char chunk_uuid[BTRFS_UUID_SIZE];
+ int ret;
+
+ /* We rely on cfg->fs_uuid and chunk_uuid to fsid and chunk uuid */
+ BUG_ON(!cfg->fs_uuid || !cfg->chunk_uuid);
+ ret = uuid_parse(cfg->fs_uuid, fsid);
+ if (ret)
+ return -EINVAL;
+ ret = uuid_parse(cfg->chunk_uuid, chunk_uuid);
+ if (ret)
+ return -EINVAL;
+
+ memset(buf->data, 0, cfg->nodesize);
+ buf->len = cfg->nodesize;
+ btrfs_set_header_bytenr(buf, bytenr);
+ btrfs_set_header_generation(buf, 1);
+ btrfs_set_header_backref_rev(buf, BTRFS_MIXED_BACKREF_REV);
+ btrfs_set_header_owner(buf, owner);
+ btrfs_set_header_flags(buf, BTRFS_HEADER_FLAG_WRITTEN);
+ write_extent_buffer(buf, chunk_uuid, btrfs_header_chunk_tree_uuid(buf),
+ BTRFS_UUID_SIZE);
+ write_extent_buffer(buf, fsid, btrfs_header_fsid(), BTRFS_FSID_SIZE);
+ return 0;
+}
+
+static inline int write_temp_extent_buffer(int fd, struct extent_buffer *buf,
+ u64 bytenr)
+{
+ int ret;
+
+ csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
+
+ /* Temporary extent buffer is always mapped 1:1 on disk */
+ ret = pwrite(fd, buf->data, buf->len, bytenr);
+ if (ret < buf->len)
+ ret = (ret < 0 ? ret : -EIO);
+ else
+ ret = 0;
+ return ret;
+}
+
+/*
+ * Insert a root item for temporary tree root
+ *
+ * Only used in make_btrfs_v2().
+ */
+static void insert_temp_root_item(struct extent_buffer *buf,
+ struct btrfs_mkfs_config *cfg,
+ int *slot, u32 *itemoff, u64 objectid,
+ u64 bytenr)
+{
+ struct btrfs_root_item root_item;
+ struct btrfs_inode_item *inode_item;
+ struct btrfs_disk_key disk_key;
+
+ btrfs_set_header_nritems(buf, *slot + 1);
+ (*itemoff) -= sizeof(root_item);
+ memset(&root_item, 0, sizeof(root_item));
+ inode_item = &root_item.inode;
+ btrfs_set_stack_inode_generation(inode_item, 1);
+ btrfs_set_stack_inode_size(inode_item, 3);
+ btrfs_set_stack_inode_nlink(inode_item, 1);
+ btrfs_set_stack_inode_nbytes(inode_item, cfg->nodesize);
+ btrfs_set_stack_inode_mode(inode_item, S_IFDIR | 0755);
+ btrfs_set_root_refs(&root_item, 1);
+ btrfs_set_root_used(&root_item, cfg->nodesize);
+ btrfs_set_root_generation(&root_item, 1);
+ btrfs_set_root_bytenr(&root_item, bytenr);
+
+ memset(&disk_key, 0, sizeof(disk_key));
+ btrfs_set_disk_key_type(&disk_key, BTRFS_ROOT_ITEM_KEY);
+ btrfs_set_disk_key_objectid(&disk_key, objectid);
+ btrfs_set_disk_key_offset(&disk_key, 0);
+
+ btrfs_set_item_key(buf, &disk_key, *slot);
+ btrfs_set_item_offset(buf, btrfs_item_nr(*slot), *itemoff);
+ btrfs_set_item_size(buf, btrfs_item_nr(*slot), sizeof(root_item));
+ write_extent_buffer(buf, &root_item,
+ btrfs_item_ptr_offset(buf, *slot),
+ sizeof(root_item));
+ (*slot)++;
+}
+
+static int setup_temp_root_tree(int fd, struct btrfs_mkfs_config *cfg,
+ u64 root_bytenr, u64 extent_bytenr,
+ u64 dev_bytenr, u64 fs_bytenr, u64 csum_bytenr)
+{
+ struct extent_buffer *buf = NULL;
+ u32 itemoff = __BTRFS_LEAF_DATA_SIZE(cfg->nodesize);
+ int slot = 0;
+ int ret;
+
+ /*
+ * Provided bytenr must in ascending order, or tree root will have a
+ * bad key order.
+ */
+ BUG_ON(!(root_bytenr < extent_bytenr && extent_bytenr < dev_bytenr &&
+ dev_bytenr < fs_bytenr && fs_bytenr < csum_bytenr));
+ buf = malloc(sizeof(*buf) + cfg->nodesize);
+ if (!buf)
+ return -ENOMEM;
+
+ ret = setup_temp_extent_buffer(buf, cfg, root_bytenr,
+ BTRFS_ROOT_TREE_OBJECTID);
+ if (ret < 0)
+ goto out;
+
+ insert_temp_root_item(buf, cfg, &slot, &itemoff,
+ BTRFS_EXTENT_TREE_OBJECTID, extent_bytenr);
+ insert_temp_root_item(buf, cfg, &slot, &itemoff,
+ BTRFS_DEV_TREE_OBJECTID, dev_bytenr);
+ insert_temp_root_item(buf, cfg, &slot, &itemoff,
+ BTRFS_FS_TREE_OBJECTID, fs_bytenr);
+ insert_temp_root_item(buf, cfg, &slot, &itemoff,
+ BTRFS_CSUM_TREE_OBJECTID, csum_bytenr);
+
+ ret = write_temp_extent_buffer(fd, buf, root_bytenr);
+out:
+ free(buf);
+ return ret;
+}
+
+static int insert_temp_dev_item(int fd, struct extent_buffer *buf,
+ struct btrfs_mkfs_config *cfg,
+ int *slot, u32 *itemoff)
+{
+ struct btrfs_disk_key disk_key;
+ struct btrfs_dev_item *dev_item;
+ char super_buf[BTRFS_SUPER_INFO_SIZE];
+ unsigned char dev_uuid[BTRFS_UUID_SIZE];
+ unsigned char fsid[BTRFS_FSID_SIZE];
+ struct btrfs_super_block *super = (struct btrfs_super_block *)super_buf;
+ int ret;
+
+ ret = pread(fd, super_buf, BTRFS_SUPER_INFO_SIZE, cfg->super_bytenr);
+ if (ret < BTRFS_SUPER_INFO_SIZE) {
+ ret = (ret < 0 ? -errno : -EIO);
+ goto out;
+ }
+
+ btrfs_set_header_nritems(buf, *slot + 1);
+ (*itemoff) -= sizeof(*dev_item);
+ /* setup device item 1, 0 is for replace case */
+ btrfs_set_disk_key_type(&disk_key, BTRFS_DEV_ITEM_KEY);
+ btrfs_set_disk_key_objectid(&disk_key, BTRFS_DEV_ITEMS_OBJECTID);
+ btrfs_set_disk_key_offset(&disk_key, 1);
+ btrfs_set_item_key(buf, &disk_key, *slot);
+ btrfs_set_item_offset(buf, btrfs_item_nr(*slot), *itemoff);
+ btrfs_set_item_size(buf, btrfs_item_nr(*slot), sizeof(*dev_item));
+
+ dev_item = btrfs_item_ptr(buf, *slot, struct btrfs_dev_item);
+ /* Generate device uuid */
+ uuid_generate(dev_uuid);
+ write_extent_buffer(buf, dev_uuid,
+ (unsigned long)btrfs_device_uuid(dev_item),
+ BTRFS_UUID_SIZE);
+ uuid_parse(cfg->fs_uuid, fsid);
+ write_extent_buffer(buf, fsid,
+ (unsigned long)btrfs_device_fsid(dev_item),
+ BTRFS_FSID_SIZE);
+ btrfs_set_device_id(buf, dev_item, 1);
+ btrfs_set_device_generation(buf, dev_item, 0);
+ btrfs_set_device_total_bytes(buf, dev_item, cfg->num_bytes);
+ /*
+ * The number must match the initial SYSTEM and META chunk size
+ */
+ btrfs_set_device_bytes_used(buf, dev_item,
+ BTRFS_MKFS_SYSTEM_GROUP_SIZE +
+ BTRFS_CONVERT_META_GROUP_SIZE);
+ btrfs_set_device_io_align(buf, dev_item, cfg->sectorsize);
+ btrfs_set_device_io_width(buf, dev_item, cfg->sectorsize);
+ btrfs_set_device_sector_size(buf, dev_item, cfg->sectorsize);
+ btrfs_set_device_type(buf, dev_item, 0);
+
+ /* Super dev_item is not complete, copy the complete one to sb */
+ read_extent_buffer(buf, &super->dev_item, (unsigned long)dev_item,
+ sizeof(*dev_item));
+ ret = write_temp_super(fd, super, cfg->super_bytenr);
+ (*slot)++;
+out:
+ return ret;
+}
+
+static int insert_temp_chunk_item(int fd, struct extent_buffer *buf,
+ struct btrfs_mkfs_config *cfg,
+ int *slot, u32 *itemoff, u64 start, u64 len,
+ u64 type)
+{
+ struct btrfs_chunk *chunk;
+ struct btrfs_disk_key disk_key;
+ char super_buf[BTRFS_SUPER_INFO_SIZE];
+ struct btrfs_super_block *sb = (struct btrfs_super_block *)super_buf;
+ int ret = 0;
+
+ ret = pread(fd, super_buf, BTRFS_SUPER_INFO_SIZE,
+ cfg->super_bytenr);
+ if (ret < BTRFS_SUPER_INFO_SIZE) {
+ ret = (ret < 0 ? ret : -EIO);
+ return ret;
+ }
+
+ btrfs_set_header_nritems(buf, *slot + 1);
+ (*itemoff) -= btrfs_chunk_item_size(1);
+ btrfs_set_disk_key_type(&disk_key, BTRFS_CHUNK_ITEM_KEY);
+ btrfs_set_disk_key_objectid(&disk_key, BTRFS_FIRST_CHUNK_TREE_OBJECTID);
+ btrfs_set_disk_key_offset(&disk_key, start);
+ btrfs_set_item_key(buf, &disk_key, *slot);
+ btrfs_set_item_offset(buf, btrfs_item_nr(*slot), *itemoff);
+ btrfs_set_item_size(buf, btrfs_item_nr(*slot),
+ btrfs_chunk_item_size(1));
+
+ chunk = btrfs_item_ptr(buf, *slot, struct btrfs_chunk);
+ btrfs_set_chunk_length(buf, chunk, len);
+ btrfs_set_chunk_owner(buf, chunk, BTRFS_EXTENT_TREE_OBJECTID);
+ btrfs_set_chunk_stripe_len(buf, chunk, 64 * 1024);
+ btrfs_set_chunk_type(buf, chunk, type);
+ btrfs_set_chunk_io_align(buf, chunk, cfg->sectorsize);
+ btrfs_set_chunk_io_width(buf, chunk, cfg->sectorsize);
+ btrfs_set_chunk_sector_size(buf, chunk, cfg->sectorsize);
+ btrfs_set_chunk_num_stripes(buf, chunk, 1);
+ /* TODO: Support DUP profile for system chunk */
+ btrfs_set_stripe_devid_nr(buf, chunk, 0, 1);
+ /* We are doing 1:1 mapping, so start is its dev offset */
+ btrfs_set_stripe_offset_nr(buf, chunk, 0, start);
+ write_extent_buffer(buf, &sb->dev_item.uuid,
+ (unsigned long)btrfs_stripe_dev_uuid_nr(chunk, 0),
+ BTRFS_UUID_SIZE);
+ (*slot)++;
+
+ /*
+ * If it's system chunk, also copy it to super block.
+ */
+ if (type & BTRFS_BLOCK_GROUP_SYSTEM) {
+ char *cur;
+
+ cur = (char *)sb->sys_chunk_array + sb->sys_chunk_array_size;
+ memcpy(cur, &disk_key, sizeof(disk_key));
+ cur += sizeof(disk_key);
+ read_extent_buffer(buf, cur, (unsigned long int)chunk,
+ btrfs_chunk_item_size(1));
+ sb->sys_chunk_array_size += btrfs_chunk_item_size(1) +
+ sizeof(disk_key);
+
+ ret = write_temp_super(fd, sb, cfg->super_bytenr);
+ }
+ return ret;
+}
+
+static int setup_temp_chunk_tree(int fd, struct btrfs_mkfs_config *cfg,
+ u64 sys_chunk_start, u64 meta_chunk_start,
+ u64 chunk_bytenr)
+{
+ struct extent_buffer *buf = NULL;
+ u32 itemoff = __BTRFS_LEAF_DATA_SIZE(cfg->nodesize);
+ int slot = 0;
+ int ret;
+
+ /* Must ensure SYS chunk starts before META chunk */
+ BUG_ON(meta_chunk_start < sys_chunk_start);
+ buf = malloc(sizeof(*buf) + cfg->nodesize);
+ if (!buf)
+ return -ENOMEM;
+ ret = setup_temp_extent_buffer(buf, cfg, chunk_bytenr,
+ BTRFS_CHUNK_TREE_OBJECTID);
+ if (ret < 0)
+ goto out;
+
+ ret = insert_temp_dev_item(fd, buf, cfg, &slot, &itemoff);
+ if (ret < 0)
+ goto out;
+ ret = insert_temp_chunk_item(fd, buf, cfg, &slot, &itemoff,
+ sys_chunk_start,
+ BTRFS_MKFS_SYSTEM_GROUP_SIZE,
+ BTRFS_BLOCK_GROUP_SYSTEM);
+ if (ret < 0)
+ goto out;
+ ret = insert_temp_chunk_item(fd, buf, cfg, &slot, &itemoff,
+ meta_chunk_start,
+ BTRFS_CONVERT_META_GROUP_SIZE,
+ BTRFS_BLOCK_GROUP_METADATA);
+ if (ret < 0)
+ goto out;
+ ret = write_temp_extent_buffer(fd, buf, chunk_bytenr);
+
+out:
+ free(buf);
+ return ret;
+}
+
+static void insert_temp_dev_extent(struct extent_buffer *buf,
+ int *slot, u32 *itemoff, u64 start, u64 len)
+{
+ struct btrfs_dev_extent *dev_extent;
+ struct btrfs_disk_key disk_key;
+
+ btrfs_set_header_nritems(buf, *slot + 1);
+ (*itemoff) -= sizeof(*dev_extent);
+ btrfs_set_disk_key_type(&disk_key, BTRFS_DEV_EXTENT_KEY);
+ btrfs_set_disk_key_objectid(&disk_key, 1);
+ btrfs_set_disk_key_offset(&disk_key, start);
+ btrfs_set_item_key(buf, &disk_key, *slot);
+ btrfs_set_item_offset(buf, btrfs_item_nr(*slot), *itemoff);
+ btrfs_set_item_size(buf, btrfs_item_nr(*slot), sizeof(*dev_extent));
+
+ dev_extent = btrfs_item_ptr(buf, *slot, struct btrfs_dev_extent);
+ btrfs_set_dev_extent_chunk_objectid(buf, dev_extent,
+ BTRFS_FIRST_CHUNK_TREE_OBJECTID);
+ btrfs_set_dev_extent_length(buf, dev_extent, len);
+ btrfs_set_dev_extent_chunk_offset(buf, dev_extent, start);
+ btrfs_set_dev_extent_chunk_tree(buf, dev_extent,
+ BTRFS_CHUNK_TREE_OBJECTID);
+ (*slot)++;
+}
+
+static int setup_temp_dev_tree(int fd, struct btrfs_mkfs_config *cfg,
+ u64 sys_chunk_start, u64 meta_chunk_start,
+ u64 dev_bytenr)
+{
+ struct extent_buffer *buf = NULL;
+ u32 itemoff = __BTRFS_LEAF_DATA_SIZE(cfg->nodesize);
+ int slot = 0;
+ int ret;
+
+ /* Must ensure SYS chunk starts before META chunk */
+ BUG_ON(meta_chunk_start < sys_chunk_start);
+ buf = malloc(sizeof(*buf) + cfg->nodesize);
+ if (!buf)
+ return -ENOMEM;
+ ret = setup_temp_extent_buffer(buf, cfg, dev_bytenr,
+ BTRFS_DEV_TREE_OBJECTID);
+ if (ret < 0)
+ goto out;
+ insert_temp_dev_extent(buf, &slot, &itemoff, sys_chunk_start,
+ BTRFS_MKFS_SYSTEM_GROUP_SIZE);
+ insert_temp_dev_extent(buf, &slot, &itemoff, meta_chunk_start,
+ BTRFS_CONVERT_META_GROUP_SIZE);
+ ret = write_temp_extent_buffer(fd, buf, dev_bytenr);
+out:
+ free(buf);
+ return ret;
+}
+
+static int setup_temp_fs_tree(int fd, struct btrfs_mkfs_config *cfg,
+ u64 fs_bytenr)
+{
+ struct extent_buffer *buf = NULL;
+ int ret;
+
+ buf = malloc(sizeof(*buf) + cfg->nodesize);
+ if (!buf)
+ return -ENOMEM;
+ ret = setup_temp_extent_buffer(buf, cfg, fs_bytenr,
+ BTRFS_FS_TREE_OBJECTID);
+ if (ret < 0)
+ goto out;
+ /*
+ * Temporary fs tree is completely empty.
+ */
+ ret = write_temp_extent_buffer(fd, buf, fs_bytenr);
+out:
+ free(buf);
+ return ret;
+}
+
+static int setup_temp_csum_tree(int fd, struct btrfs_mkfs_config *cfg,
+ u64 csum_bytenr)
+{
+ struct extent_buffer *buf = NULL;
+ int ret;
+
+ buf = malloc(sizeof(*buf) + cfg->nodesize);
+ if (!buf)
+ return -ENOMEM;
+ ret = setup_temp_extent_buffer(buf, cfg, csum_bytenr,
+ BTRFS_CSUM_TREE_OBJECTID);
+ if (ret < 0)
+ goto out;
+ /*
+ * Temporary csum tree is completely empty.
+ */
+ ret = write_temp_extent_buffer(fd, buf, csum_bytenr);
+out:
+ free(buf);
+ return ret;
+}
+
+/*
+ * Insert one temporary extent item.
+ *
+ * NOTE: if skinny_metadata is not enabled, this function must be called
+ * after all other trees are initialized.
+ * Or fs without skinny-metadata will be screwed up.
+ */
+static int insert_temp_extent_item(int fd, struct extent_buffer *buf,
+ struct btrfs_mkfs_config *cfg,
+ int *slot, u32 *itemoff, u64 bytenr,
+ u64 ref_root)
+{
+ struct extent_buffer *tmp;
+ struct btrfs_extent_item *ei;
+ struct btrfs_extent_inline_ref *iref;
+ struct btrfs_disk_key disk_key;
+ struct btrfs_disk_key tree_info_key;
+ struct btrfs_tree_block_info *info;
+ int itemsize;
+ int skinny_metadata = cfg->features &
+ BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA;
+ int ret;
+
+ if (skinny_metadata)
+ itemsize = sizeof(*ei) + sizeof(*iref);
+ else
+ itemsize = sizeof(*ei) + sizeof(*iref) +
+ sizeof(struct btrfs_tree_block_info);
+
+ btrfs_set_header_nritems(buf, *slot + 1);
+ *(itemoff) -= itemsize;
+
+ if (skinny_metadata) {
+ btrfs_set_disk_key_type(&disk_key, BTRFS_METADATA_ITEM_KEY);
+ btrfs_set_disk_key_offset(&disk_key, 0);
+ } else {
+ btrfs_set_disk_key_type(&disk_key, BTRFS_EXTENT_ITEM_KEY);
+ btrfs_set_disk_key_offset(&disk_key, cfg->nodesize);
+ }
+ btrfs_set_disk_key_objectid(&disk_key, bytenr);
+
+ btrfs_set_item_key(buf, &disk_key, *slot);
+ btrfs_set_item_offset(buf, btrfs_item_nr(*slot), *itemoff);
+ btrfs_set_item_size(buf, btrfs_item_nr(*slot), itemsize);
+
+ ei = btrfs_item_ptr(buf, *slot, struct btrfs_extent_item);
+ btrfs_set_extent_refs(buf, ei, 1);
+ btrfs_set_extent_generation(buf, ei, 1);
+ btrfs_set_extent_flags(buf, ei, BTRFS_EXTENT_FLAG_TREE_BLOCK);
+
+ if (skinny_metadata) {
+ iref = (struct btrfs_extent_inline_ref *)(ei + 1);
+ } else {
+ info = (struct btrfs_tree_block_info *)(ei + 1);
+ iref = (struct btrfs_extent_inline_ref *)(info + 1);
+ }
+ btrfs_set_extent_inline_ref_type(buf, iref,
+ BTRFS_TREE_BLOCK_REF_KEY);
+ btrfs_set_extent_inline_ref_offset(buf, iref, ref_root);
+
+ (*slot)++;
+ if (skinny_metadata)
+ return 0;
+
+ /*
+ * Lastly, check the tree block key by read the tree block
+ * Since we do 1:1 mapping for convert case, we can directly
+ * read the bytenr from disk
+ */
+ tmp = malloc(sizeof(*tmp) + cfg->nodesize);
+ if (!tmp)
+ return -ENOMEM;
+ ret = setup_temp_extent_buffer(tmp, cfg, bytenr, ref_root);
+ if (ret < 0)
+ goto out;
+ ret = pread(fd, tmp->data, cfg->nodesize, bytenr);
+ if (ret < cfg->nodesize) {
+ ret = (ret < 0 ? -errno : -EIO);
+ goto out;
+ }
+ if (btrfs_header_nritems(tmp) == 0) {
+ btrfs_set_disk_key_type(&tree_info_key, 0);
+ btrfs_set_disk_key_objectid(&tree_info_key, 0);
+ btrfs_set_disk_key_offset(&tree_info_key, 0);
+ } else {
+ btrfs_item_key(tmp, &tree_info_key, 0);
+ }
+ btrfs_set_tree_block_key(buf, info, &tree_info_key);
+
+out:
+ free(tmp);
+ return ret;
+}
+
+static void insert_temp_block_group(struct extent_buffer *buf,
+ struct btrfs_mkfs_config *cfg,
+ int *slot, u32 *itemoff,
+ u64 bytenr, u64 len, u64 used, u64 flag)
+{
+ struct btrfs_block_group_item bgi;
+ struct btrfs_disk_key disk_key;
+
+ btrfs_set_header_nritems(buf, *slot + 1);
+ (*itemoff) -= sizeof(bgi);
+ btrfs_set_disk_key_type(&disk_key, BTRFS_BLOCK_GROUP_ITEM_KEY);
+ btrfs_set_disk_key_objectid(&disk_key, bytenr);
+ btrfs_set_disk_key_offset(&disk_key, len);
+ btrfs_set_item_key(buf, &disk_key, *slot);
+ btrfs_set_item_offset(buf, btrfs_item_nr(*slot), *itemoff);
+ btrfs_set_item_size(buf, btrfs_item_nr(*slot), sizeof(bgi));
+
+ btrfs_set_block_group_flags(&bgi, flag);
+ btrfs_set_block_group_used(&bgi, used);
+ btrfs_set_block_group_chunk_objectid(&bgi,
+ BTRFS_FIRST_CHUNK_TREE_OBJECTID);
+ write_extent_buffer(buf, &bgi, btrfs_item_ptr_offset(buf, *slot),
+ sizeof(bgi));
+ (*slot)++;
+}
+
+static int setup_temp_extent_tree(int fd, struct btrfs_mkfs_config *cfg,
+ u64 chunk_bytenr, u64 root_bytenr,
+ u64 extent_bytenr, u64 dev_bytenr,
+ u64 fs_bytenr, u64 csum_bytenr)
+{
+ struct extent_buffer *buf = NULL;
+ u32 itemoff = __BTRFS_LEAF_DATA_SIZE(cfg->nodesize);
+ int slot = 0;
+ int ret;
+
+ /*
+ * We must ensure provided bytenr are in ascending order,
+ * or extent tree key order will be broken.
+ */
+ BUG_ON(!(chunk_bytenr < root_bytenr && root_bytenr < extent_bytenr &&
+ extent_bytenr < dev_bytenr && dev_bytenr < fs_bytenr &&
+ fs_bytenr < csum_bytenr));
+ buf = malloc(sizeof(*buf) + cfg->nodesize);
+ if (!buf)
+ return -ENOMEM;
+
+ ret = setup_temp_extent_buffer(buf, cfg, extent_bytenr,
+ BTRFS_EXTENT_TREE_OBJECTID);
+ if (ret < 0)
+ goto out;
+
+ ret = insert_temp_extent_item(fd, buf, cfg, &slot, &itemoff,
+ chunk_bytenr, BTRFS_CHUNK_TREE_OBJECTID);
+ if (ret < 0)
+ goto out;
+
+ insert_temp_block_group(buf, cfg, &slot, &itemoff, chunk_bytenr,
+ BTRFS_MKFS_SYSTEM_GROUP_SIZE, cfg->nodesize,
+ BTRFS_BLOCK_GROUP_SYSTEM);
+
+ ret = insert_temp_extent_item(fd, buf, cfg, &slot, &itemoff,
+ root_bytenr, BTRFS_ROOT_TREE_OBJECTID);
+ if (ret < 0)
+ goto out;
+
+ /* 5 tree block used, root, extent, dev, fs and csum*/
+ insert_temp_block_group(buf, cfg, &slot, &itemoff, root_bytenr,
+ BTRFS_CONVERT_META_GROUP_SIZE, cfg->nodesize * 5,
+ BTRFS_BLOCK_GROUP_METADATA);
+
+ ret = insert_temp_extent_item(fd, buf, cfg, &slot, &itemoff,
+ extent_bytenr, BTRFS_EXTENT_TREE_OBJECTID);
+ if (ret < 0)
+ goto out;
+ ret = insert_temp_extent_item(fd, buf, cfg, &slot, &itemoff,
+ dev_bytenr, BTRFS_DEV_TREE_OBJECTID);
+ if (ret < 0)
+ goto out;
+ ret = insert_temp_extent_item(fd, buf, cfg, &slot, &itemoff,
+ fs_bytenr, BTRFS_FS_TREE_OBJECTID);
+ if (ret < 0)
+ goto out;
+ ret = insert_temp_extent_item(fd, buf, cfg, &slot, &itemoff,
+ csum_bytenr, BTRFS_CSUM_TREE_OBJECTID);
+ if (ret < 0)
+ goto out;
+
+ ret = write_temp_extent_buffer(fd, buf, extent_bytenr);
+out:
+ free(buf);
+ return ret;
+}
+
+/*
+ * Improved version of make_btrfs().
+ *
+ * This one will
+ * 1) Do chunk allocation to avoid used data
+ * And after this function, extent type matches chunk type
+ * 2) Better structured code
+ * No super long hand written codes to initialized all tree blocks
+ * Split into small blocks and reuse codes.
+ * TODO: Reuse tree operation facilities by introducing new flags
+ */
+static int make_convert_btrfs(int fd, struct btrfs_mkfs_config *cfg,
+ struct btrfs_convert_context *cctx)
+{
+ struct cache_tree *free = &cctx->free;
+ struct cache_tree *used = &cctx->used;
+ u64 sys_chunk_start;
+ u64 meta_chunk_start;
+ /* chunk tree bytenr, in system chunk */
+ u64 chunk_bytenr;
+ /* metadata trees bytenr, in metadata chunk */
+ u64 root_bytenr;
+ u64 extent_bytenr;
+ u64 dev_bytenr;
+ u64 fs_bytenr;
+ u64 csum_bytenr;
+ int ret;
+
+ /* Shouldn't happen */
+ BUG_ON(cache_tree_empty(used));
+
+ /*
+ * reserve space for temporary superblock first
+ * Here we allocate a little larger space, to keep later
+ * free space will be STRIPE_LEN aligned
+ */
+ ret = reserve_free_space(free, BTRFS_STRIPE_LEN,
+ &cfg->super_bytenr);
+ if (ret < 0)
+ goto out;
+
+ /*
+ * Then reserve system chunk space
+ * TODO: Change system group size depending on cctx->total_bytes.
+ * If using current 4M, it can only handle less than one TB for
+ * worst case and then run out of sys space.
+ */
+ ret = reserve_free_space(free, BTRFS_MKFS_SYSTEM_GROUP_SIZE,
+ &sys_chunk_start);
+ if (ret < 0)
+ goto out;
+ ret = reserve_free_space(free, BTRFS_CONVERT_META_GROUP_SIZE,
+ &meta_chunk_start);
+ if (ret < 0)
+ goto out;
+
+ /*
+ * Allocated meta/sys chunks will be mapped 1:1 with device offset.
+ *
+ * Inside the allocated metadata chunk, the layout will be:
+ * | offset | contents |
+ * -------------------------------------
+ * | +0 | tree root |
+ * | +nodesize | extent root |
+ * | +nodesize * 2 | device root |
+ * | +nodesize * 3 | fs tree |
+ * | +nodesize * 4 | csum tree |
+ * -------------------------------------
+ * Inside the allocated system chunk, the layout will be:
+ * | offset | contents |
+ * -------------------------------------
+ * | +0 | chunk root |
+ * -------------------------------------
+ */
+ chunk_bytenr = sys_chunk_start;
+ root_bytenr = meta_chunk_start;
+ extent_bytenr = meta_chunk_start + cfg->nodesize;
+ dev_bytenr = meta_chunk_start + cfg->nodesize * 2;
+ fs_bytenr = meta_chunk_start + cfg->nodesize * 3;
+ csum_bytenr = meta_chunk_start + cfg->nodesize * 4;
+
+ ret = setup_temp_super(fd, cfg, root_bytenr, chunk_bytenr);
+ if (ret < 0)
+ goto out;
+
+ ret = setup_temp_root_tree(fd, cfg, root_bytenr, extent_bytenr,
+ dev_bytenr, fs_bytenr, csum_bytenr);
+ if (ret < 0)
+ goto out;
+ ret = setup_temp_chunk_tree(fd, cfg, sys_chunk_start, meta_chunk_start,
+ chunk_bytenr);
+ if (ret < 0)
+ goto out;
+ ret = setup_temp_dev_tree(fd, cfg, sys_chunk_start, meta_chunk_start,
+ dev_bytenr);
+ if (ret < 0)
+ goto out;
+ ret = setup_temp_fs_tree(fd, cfg, fs_bytenr);
+ if (ret < 0)
+ goto out;
+ ret = setup_temp_csum_tree(fd, cfg, csum_bytenr);
+ if (ret < 0)
+ goto out;
+ /*
+ * Setup extent tree last, since it may need to read tree block key
+ * for non-skinny metadata case.
+ */
+ ret = setup_temp_extent_tree(fd, cfg, chunk_bytenr, root_bytenr,
+ extent_bytenr, dev_bytenr, fs_bytenr,
+ csum_bytenr);
+out:
+ return ret;
+}
+
+/*
* @fs_uuid - if NULL, generates a UUID, returns back the new filesystem UUID
*/
-int make_btrfs(int fd, struct btrfs_mkfs_config *cfg)
+int make_btrfs(int fd, struct btrfs_mkfs_config *cfg,
+ struct btrfs_convert_context *cctx)
{
struct btrfs_super_block super;
struct extent_buffer *buf;
@@ -206,6 +1031,8 @@ int make_btrfs(int fd, struct btrfs_mkfs_config *cfg)
BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA);
u64 num_bytes;
+ if (cctx)
+ return make_convert_btrfs(fd, cfg, cctx);
buf = malloc(sizeof(*buf) + max(cfg->sectorsize, cfg->nodesize));
if (!buf)
return -ENOMEM;
@@ -218,13 +1045,12 @@ int make_btrfs(int fd, struct btrfs_mkfs_config *cfg)
num_bytes = (cfg->num_bytes / cfg->sectorsize) * cfg->sectorsize;
if (cfg->fs_uuid && *cfg->fs_uuid) {
if (uuid_parse(cfg->fs_uuid, super.fsid) != 0) {
- fprintf(stderr, "could not parse UUID: %s\n",
- cfg->fs_uuid);
+ error("cannot not parse UUID: %s", cfg->fs_uuid);
ret = -EINVAL;
goto out;
}
if (!test_uuid_unique(cfg->fs_uuid)) {
- fprintf(stderr, "non-unique UUID: %s\n", cfg->fs_uuid);
+ error("non-unique UUID: %s", cfg->fs_uuid);
ret = -EBUSY;
goto out;
}
@@ -1455,7 +2281,7 @@ int check_mounted_where(int fd, const char *file, char *where, int size,
return ret;
}
- /* iterate over the list of currently mountes filesystems */
+ /* iterate over the list of currently mounted filesystems */
if ((f = setmntent ("/proc/self/mounts", "r")) == NULL)
return -errno;
@@ -1713,8 +2539,8 @@ static int check_label(const char *input)
int len = strlen(input);
if (len > BTRFS_LABEL_SIZE - 1) {
- fprintf(stderr, "ERROR: Label %s is too long (max %d)\n",
- input, BTRFS_LABEL_SIZE - 1);
+ error("label %s is too long (max %d)", input,
+ BTRFS_LABEL_SIZE - 1);
return -1;
}
@@ -1729,12 +2555,11 @@ static int set_label_unmounted(const char *dev, const char *label)
ret = check_mounted(dev);
if (ret < 0) {
- fprintf(stderr, "FATAL: error checking %s mount status\n", dev);
+ error("checking mount status of %s failed: %d", dev, ret);
return -1;
}
if (ret > 0) {
- fprintf(stderr, "ERROR: dev %s is mounted, use mount point\n",
- dev);
+ error("device %s is mounted, use mount point", dev);
return -1;
}
@@ -1762,15 +2587,15 @@ static int set_label_mounted(const char *mount_path, const char *labelp)
fd = open(mount_path, O_RDONLY | O_NOATIME);
if (fd < 0) {
- fprintf(stderr, "ERROR: unable to access '%s'\n", mount_path);
+ error("unable to access %s: %s", mount_path, strerror(errno));
return -1;
}
memset(label, 0, sizeof(label));
__strncpy_null(label, labelp, BTRFS_LABEL_SIZE - 1);
if (ioctl(fd, BTRFS_IOC_SET_FSLABEL, label) < 0) {
- fprintf(stderr, "ERROR: unable to set label %s\n",
- strerror(errno));
+ error("unable to set label of %s: %s", mount_path,
+ strerror(errno));
close(fd);
return -1;
}
@@ -1786,7 +2611,7 @@ int get_label_unmounted(const char *dev, char *label)
ret = check_mounted(dev);
if (ret < 0) {
- fprintf(stderr, "FATAL: error checking %s mount status\n", dev);
+ error("checking mount status of %s failed: %d", dev, ret);
return -1;
}
@@ -1818,7 +2643,7 @@ int get_label_mounted(const char *mount_path, char *labelp)
fd = open(mount_path, O_RDONLY | O_NOATIME);
if (fd < 0) {
- fprintf(stderr, "ERROR: unable to access '%s'\n", mount_path);
+ error("unable to access %s: %s", mount_path, strerror(errno));
return -1;
}
@@ -1826,7 +2651,7 @@ int get_label_mounted(const char *mount_path, char *labelp)
ret = ioctl(fd, BTRFS_IOC_GET_FSLABEL, label);
if (ret < 0) {
if (errno != ENOTTY)
- fprintf(stderr, "ERROR: unable to get label %s\n",
+ error("unable to get label of %s: %s", mount_path,
strerror(errno));
ret = -errno;
close(fd);
@@ -1889,21 +2714,20 @@ u64 parse_size(char *s)
u64 ret;
if (!s) {
- fprintf(stderr, "ERROR: Size value is empty\n");
+ error("size value is empty");
exit(1);
}
if (s[0] == '-') {
- fprintf(stderr,
- "ERROR: Size value '%s' is less equal than 0\n", s);
+ error("size value '%s' is less equal than 0", s);
exit(1);
}
ret = strtoull(s, &endptr, 10);
if (endptr == s) {
- fprintf(stderr, "ERROR: Size value '%s' is invalid\n", s);
+ error("size value '%s' is invalid", s);
exit(1);
}
if (endptr[0] && endptr[1]) {
- fprintf(stderr, "ERROR: Illegal suffix contains character '%c' in wrong position\n",
+ error("illegal suffix contains character '%c' in wrong position",
endptr[1]);
exit(1);
}
@@ -1912,8 +2736,7 @@ u64 parse_size(char *s)
* need to call strtoull to get the real size
*/
if (errno == ERANGE && ret == ULLONG_MAX) {
- fprintf(stderr,
- "ERROR: Size value '%s' is too large for u64\n", s);
+ error("size value '%s' is too large for u64", s);
exit(1);
}
if (endptr[0]) {
@@ -1940,15 +2763,13 @@ u64 parse_size(char *s)
case 'b':
break;
default:
- fprintf(stderr, "ERROR: Unknown size descriptor '%c'\n",
- c);
+ error("unknown size descriptor '%c'", c);
exit(1);
}
}
/* Check whether ret * mult overflow */
if (fls64(ret) + fls64(mult) - 1 > 64) {
- fprintf(stderr,
- "ERROR: Size value '%s' is too large for u64\n", s);
+ error("size value '%s' is too large for u64", s);
exit(1);
}
ret *= mult;
@@ -2000,7 +2821,7 @@ path:
return id;
err:
- fprintf(stderr, "ERROR: invalid qgroupid or subvolume path: %s\n", p);
+ error("invalid qgroupid or subvolume path: %s", p);
exit(-1);
}
@@ -2517,45 +3338,43 @@ int test_dev_for_mkfs(const char *file, int force_overwrite)
ret = is_swap_device(file);
if (ret < 0) {
- fprintf(stderr, "ERROR: checking status of %s: %s\n", file,
- strerror(-ret));
+ error("checking status of %s: %s", file, strerror(-ret));
return 1;
}
if (ret == 1) {
- fprintf(stderr, "ERROR: %s is a swap device\n", file);
+ error("%s is a swap device", file);
return 1;
}
if (!force_overwrite) {
if (check_overwrite(file)) {
- fprintf(stderr, "Use the -f option to force overwrite.\n");
+ error("use the -f option to force overwrite of %s",
+ file);
return 1;
}
}
ret = check_mounted(file);
if (ret < 0) {
- fprintf(stderr, "ERROR: checking mount status of %s: %s\n",
- file, strerror(-ret));
+ error("cannot check mount status of %s: %s", file,
+ strerror(-ret));
return 1;
}
if (ret == 1) {
- fprintf(stderr, "ERROR: %s is mounted\n", file);
+ error("%s is mounted", file);
return 1;
}
/* check if the device is busy */
fd = open(file, O_RDWR|O_EXCL);
if (fd < 0) {
- fprintf(stderr, "ERROR: unable to open %s: %s\n", file,
- strerror(errno));
+ error("unable to open %s: %s", file, strerror(errno));
return 1;
}
if (fstat(fd, &st)) {
- fprintf(stderr, "ERROR: unable to stat %s: %s\n", file,
- strerror(errno));
+ error("unable to stat %s: %s", file, strerror(errno));
close(fd);
return 1;
}
if (!S_ISBLK(st.st_mode)) {
- fprintf(stderr, "ERROR: %s is not a block device\n", file);
+ error("%s is not a block device", file);
close(fd);
return 1;
}
@@ -2578,7 +3397,7 @@ int btrfs_scan_lblkid(void)
return 0;
if (blkid_get_cache(&cache, NULL) < 0) {
- printf("ERROR: lblkid cache get failed\n");
+ error("blkid cache get failed");
return 1;
}
blkid_probe_all(cache);
@@ -2593,13 +3412,13 @@ int btrfs_scan_lblkid(void)
fd = open(path, O_RDONLY);
if (fd < 0) {
- printf("ERROR: could not open %s\n", path);
+ error("cannot open %s: %s", path, strerror(errno));
continue;
}
ret = btrfs_scan_one_device(fd, path, &tmp_devices,
&num_devices, BTRFS_SUPER_INFO_OFFSET, 0);
if (ret) {
- printf("ERROR: could not scan %s\n", path);
+ error("cannot scan %s: %s", path, strerror(-ret));
close (fd);
continue;
}
@@ -2679,8 +3498,7 @@ int lookup_ino_rootid(int fd, u64 *rootid)
ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args);
if (ret < 0) {
- fprintf(stderr, "ERROR: Failed to lookup root id - %s\n",
- strerror(errno));
+ error("failed to lookup root id: %s", strerror(errno));
return ret;
}
@@ -2931,24 +3749,20 @@ int btrfs_tree_search2_ioctl_supported(int fd)
int btrfs_check_nodesize(u32 nodesize, u32 sectorsize, u64 features)
{
if (nodesize < sectorsize) {
- fprintf(stderr,
- "ERROR: Illegal nodesize %u (smaller than %u)\n",
- nodesize, sectorsize);
+ error("illegal nodesize %u (smaller than %u)",
+ nodesize, sectorsize);
return -1;
} else if (nodesize > BTRFS_MAX_METADATA_BLOCKSIZE) {
- fprintf(stderr,
- "ERROR: Illegal nodesize %u (larger than %u)\n",
+ error("illegal nodesize %u (larger than %u)",
nodesize, BTRFS_MAX_METADATA_BLOCKSIZE);
return -1;
} else if (nodesize & (sectorsize - 1)) {
- fprintf(stderr,
- "ERROR: Illegal nodesize %u (not aligned to %u)\n",
+ error("illegal nodesize %u (not aligned to %u)",
nodesize, sectorsize);
return -1;
} else if (features & BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS &&
nodesize != sectorsize) {
- fprintf(stderr,
- "ERROR: Illegal nodesize %u (not equal to %u for mixed block group)\n",
+ error("illegal nodesize %u (not equal to %u for mixed block group)",
nodesize, sectorsize);
return -1;
}
@@ -3116,6 +3930,24 @@ void clean_args_no_options(int argc, char *argv[], const char * const *usagestr)
}
}
+/*
+ * Same as clean_args_no_options but pass through arguments that could look
+ * like short options. Eg. reisze which takes a negative resize argument like
+ * '-123M' .
+ *
+ * This accepts only two forms:
+ * - "-- option1 option2 ..."
+ * - "option1 option2 ..."
+ */
+void clean_args_no_options_relaxed(int argc, char *argv[], const char * const *usagestr)
+{
+ if (argc <= 1)
+ return;
+
+ if (strcmp(argv[1], "--") == 0)
+ optind = 2;
+}
+
/* Subvolume helper functions */
/*
* test if name is a correct subvolume name
@@ -3240,3 +4072,62 @@ out:
return ret;
}
+
+void init_rand_seed(u64 seed)
+{
+ int i;
+
+ /* only use the last 48 bits */
+ for (i = 0; i < 3; i++) {
+ rand_seed[i] = (unsigned short)(seed ^ (unsigned short)(-1));
+ seed >>= 16;
+ }
+ rand_seed_initlized = 1;
+}
+
+static void __init_seed(void)
+{
+ struct timeval tv;
+ int ret;
+ int fd;
+
+ if(rand_seed_initlized)
+ return;
+ /* Use urandom as primary seed source. */
+ fd = open("/dev/urandom", O_RDONLY);
+ if (fd >= 0) {
+ ret = read(fd, rand_seed, sizeof(rand_seed));
+ close(fd);
+ if (ret < sizeof(rand_seed))
+ goto fallback;
+ } else {
+fallback:
+ /* Use time and pid as fallback seed */
+ warning("failed to read /dev/urandom, use time and pid as random seed");
+ gettimeofday(&tv, 0);
+ rand_seed[0] = getpid() ^ (tv.tv_sec & 0xFFFF);
+ rand_seed[1] = getppid() ^ (tv.tv_usec & 0xFFFF);
+ rand_seed[2] = (tv.tv_sec ^ tv.tv_usec) >> 16;
+ }
+ rand_seed_initlized = 1;
+}
+
+u32 rand_u32(void)
+{
+ __init_seed();
+ /*
+ * Don't use nrand48, its range is [0,2^31) The highest bit will alwasy
+ * be 0. Use jrand48 to include the highest bit.
+ */
+ return (u32)jrand48(rand_seed);
+}
+
+unsigned int rand_range(unsigned int upper)
+{
+ __init_seed();
+ /*
+ * Use the full 48bits to mod, which would be more uniformly
+ * distributed
+ */
+ return (unsigned int)(jrand48(rand_seed) % upper);
+}
diff --git a/utils.h b/utils.h
index 7a392c4..98bfb34 100644
--- a/utils.h
+++ b/utils.h
@@ -46,6 +46,8 @@
| BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA \
| BTRFS_FEATURE_INCOMPAT_NO_HOLES)
+#define BTRFS_CONVERT_META_GROUP_SIZE (32 * 1024 * 1024)
+
#define BTRFS_FEATURE_LIST_ALL (1ULL << 63)
#define BTRFS_SCAN_MOUNTED (1ULL << 0)
@@ -123,7 +125,30 @@ struct btrfs_mkfs_config {
u64 super_bytenr;
};
-int make_btrfs(int fd, struct btrfs_mkfs_config *cfg);
+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;
+};
+
+int make_btrfs(int fd, struct btrfs_mkfs_config *cfg,
+ struct btrfs_convert_context *cctx);
int btrfs_make_root_dir(struct btrfs_trans_handle *trans,
struct btrfs_root *root, u64 objectid);
int btrfs_prepare_device(int fd, const char *file, int zero_end,
@@ -209,8 +234,9 @@ int get_subvol_info(const char *fullpath, struct root_info *get_ri);
*/
static inline u64 btrfs_min_global_blk_rsv_size(u32 nodesize)
{
- return nodesize << 10;
+ return (u64)nodesize << 10;
}
+
static inline u64 btrfs_min_dev_size(u32 nodesize)
{
return 2 * (BTRFS_MKFS_SYSTEM_GROUP_SIZE +
@@ -277,6 +303,8 @@ const char *get_argv0_buf(void);
unsigned int get_unit_mode_from_arg(int *argc, char *argv[], int df_mode);
void clean_args_no_options(int argc, char *argv[], const char * const *usage);
+void clean_args_no_options_relaxed(int argc, char *argv[],
+ const char * const *usagestr);
int string_is_numerical(const char *str);
__attribute__ ((format (printf, 1, 2)))
@@ -337,4 +365,38 @@ static inline int error_on(int condition, const char *fmt, ...)
return 1;
}
+/* Pseudo random number generator wrappers */
+u32 rand_u32(void);
+
+static inline int rand_int(void)
+{
+ return (int)(rand_u32());
+}
+
+static inline u64 rand_u64(void)
+{
+ u64 ret = 0;
+
+ ret += rand_u32();
+ ret <<= 32;
+ ret += rand_u32();
+ return ret;
+}
+
+static inline u16 rand_u16(void)
+{
+ return (u16)(rand_u32());
+}
+
+static inline u8 rand_u8(void)
+{
+ return (u8)(rand_u32());
+}
+
+/* Return random number in range [0, limit) */
+unsigned int rand_range(unsigned int upper);
+
+/* Also allow setting the seed manually */
+void init_rand_seed(u64 seed);
+
#endif
diff --git a/uuid-tree.c b/uuid-tree.c
index 39c3f3f..8d0b917 100644
--- a/uuid-tree.c
+++ b/uuid-tree.c
@@ -72,7 +72,7 @@ static int btrfs_uuid_tree_lookup_any(int fd, const u8 *uuid, u8 type,
goto out;
}
search_header = (struct btrfs_ioctl_search_header *)(search_arg.buf);
- item_size = search_header->len;
+ item_size = btrfs_search_header_len(search_header);
if ((item_size & (sizeof(u64) - 1)) || item_size == 0) {
printf("btrfs: uuid item with illegal size %lu!\n",
(unsigned long)item_size);
diff --git a/version.sh b/version.sh
index 6a5d659..cdb847c 100755
--- a/version.sh
+++ b/version.sh
@@ -6,7 +6,7 @@
# Copyright 2008, Oracle
# Released under the GNU GPLv2
-v="v4.5.2"
+v="v4.6.1"
opt=$1
diff --git a/volumes.c b/volumes.c
index 4d22db2..ccfa732 100644
--- a/volumes.c
+++ b/volumes.c
@@ -399,7 +399,7 @@ static int btrfs_alloc_dev_extent(struct btrfs_trans_handle *trans,
struct btrfs_device *device,
u64 chunk_tree, u64 chunk_objectid,
u64 chunk_offset,
- u64 num_bytes, u64 *start)
+ u64 num_bytes, u64 *start, int convert)
{
int ret;
struct btrfs_path *path;
@@ -412,9 +412,15 @@ static int btrfs_alloc_dev_extent(struct btrfs_trans_handle *trans,
if (!path)
return -ENOMEM;
- ret = find_free_dev_extent(trans, device, path, num_bytes, start);
- if (ret) {
- goto err;
+ /*
+ * For convert case, just skip search free dev_extent, as caller
+ * is responsible to make sure it's free.
+ */
+ if (!convert) {
+ ret = find_free_dev_extent(trans, device, path, num_bytes,
+ start);
+ if (ret)
+ goto err;
}
key.objectid = device->devid;
@@ -973,7 +979,7 @@ again:
ret = btrfs_alloc_dev_extent(trans, device,
info->chunk_root->root_key.objectid,
BTRFS_FIRST_CHUNK_TREE_OBJECTID, key.offset,
- calc_size, &dev_offset);
+ calc_size, &dev_offset, 0);
BUG_ON(ret);
device->bytes_used += calc_size;
@@ -1029,9 +1035,17 @@ again:
return ret;
}
+/*
+ * Alloc a DATA chunk with SINGLE profile.
+ *
+ * If 'convert' is set, it will alloc a chunk with 1:1 mapping
+ * (btrfs logical bytenr == on-disk bytenr)
+ * For that case, caller must make sure the chunk and dev_extent are not
+ * occupied.
+ */
int btrfs_alloc_data_chunk(struct btrfs_trans_handle *trans,
struct btrfs_root *extent_root, u64 *start,
- u64 num_bytes, u64 type)
+ u64 num_bytes, u64 type, int convert)
{
u64 dev_offset;
struct btrfs_fs_info *info = extent_root->fs_info;
@@ -1052,10 +1066,17 @@ int btrfs_alloc_data_chunk(struct btrfs_trans_handle *trans,
key.objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID;
key.type = BTRFS_CHUNK_ITEM_KEY;
- ret = find_next_chunk(chunk_root, BTRFS_FIRST_CHUNK_TREE_OBJECTID,
- &key.offset);
- if (ret)
- return ret;
+ if (convert) {
+ BUG_ON(*start != round_down(*start, extent_root->sectorsize));
+ key.offset = *start;
+ dev_offset = *start;
+ } else {
+ ret = find_next_chunk(chunk_root,
+ BTRFS_FIRST_CHUNK_TREE_OBJECTID,
+ &key.offset);
+ if (ret)
+ return ret;
+ }
chunk = kmalloc(btrfs_chunk_item_size(num_stripes), GFP_NOFS);
if (!chunk)
@@ -1080,7 +1101,7 @@ int btrfs_alloc_data_chunk(struct btrfs_trans_handle *trans,
ret = btrfs_alloc_dev_extent(trans, device,
info->chunk_root->root_key.objectid,
BTRFS_FIRST_CHUNK_TREE_OBJECTID, key.offset,
- calc_size, &dev_offset);
+ calc_size, &dev_offset, convert);
BUG_ON(ret);
device->bytes_used += calc_size;
@@ -1117,7 +1138,8 @@ int btrfs_alloc_data_chunk(struct btrfs_trans_handle *trans,
ret = btrfs_insert_item(trans, chunk_root, &key, chunk,
btrfs_chunk_item_size(num_stripes));
BUG_ON(ret);
- *start = key.offset;
+ if (!convert)
+ *start = key.offset;
map->ce.start = key.offset;
map->ce.size = num_bytes;
@@ -1587,7 +1609,92 @@ static struct btrfs_device *fill_missing_device(u64 devid)
}
/*
- * Slot is used to verfy the chunk item is valid
+ * slot == -1: SYSTEM chunk
+ * return -EIO on error, otherwise return 0
+ */
+static int btrfs_check_chunk_valid(struct btrfs_root *root,
+ struct extent_buffer *leaf,
+ struct btrfs_chunk *chunk,
+ int slot, u64 logical)
+{
+ u64 length;
+ u64 stripe_len;
+ u16 num_stripes;
+ u16 sub_stripes;
+ u64 type;
+
+ length = btrfs_chunk_length(leaf, chunk);
+ stripe_len = btrfs_chunk_stripe_len(leaf, chunk);
+ num_stripes = btrfs_chunk_num_stripes(leaf, chunk);
+ sub_stripes = btrfs_chunk_sub_stripes(leaf, chunk);
+ type = btrfs_chunk_type(leaf, chunk);
+
+ /*
+ * These valid checks may be insufficient to cover every corner cases.
+ */
+ if (!IS_ALIGNED(logical, root->sectorsize)) {
+ error("invalid chunk logical %llu", logical);
+ return -EIO;
+ }
+ if (btrfs_chunk_sector_size(leaf, chunk) != root->sectorsize) {
+ error("invalid chunk sectorsize %llu",
+ (unsigned long long)btrfs_chunk_sector_size(leaf, chunk));
+ return -EIO;
+ }
+ if (!length || !IS_ALIGNED(length, root->sectorsize)) {
+ error("invalid chunk length %llu", length);
+ return -EIO;
+ }
+ if (stripe_len != BTRFS_STRIPE_LEN) {
+ error("invalid chunk stripe length: %llu", stripe_len);
+ return -EIO;
+ }
+ /* Check on chunk item type */
+ if (slot == -1 && (type & BTRFS_BLOCK_GROUP_SYSTEM) == 0) {
+ error("invalid chunk type %llu", type);
+ return -EIO;
+ }
+ if (type & ~(BTRFS_BLOCK_GROUP_TYPE_MASK |
+ BTRFS_BLOCK_GROUP_PROFILE_MASK)) {
+ error("unrecognized chunk type: %llu",
+ ~(BTRFS_BLOCK_GROUP_TYPE_MASK |
+ BTRFS_BLOCK_GROUP_PROFILE_MASK) & type);
+ return -EIO;
+ }
+ /*
+ * Btrfs_chunk contains at least one stripe, and for sys_chunk
+ * it can't exceed the system chunk array size
+ * For normal chunk, it should match its chunk item size.
+ */
+ if (num_stripes < 1 ||
+ (slot == -1 && sizeof(struct btrfs_stripe) * num_stripes >
+ BTRFS_SYSTEM_CHUNK_ARRAY_SIZE) ||
+ (slot >= 0 && sizeof(struct btrfs_stripe) * (num_stripes - 1) >
+ btrfs_item_size_nr(leaf, slot))) {
+ error("invalid num_stripes: %u", num_stripes);
+ return -EIO;
+ }
+ /*
+ * Device number check against profile
+ */
+ if ((type & BTRFS_BLOCK_GROUP_RAID10 && sub_stripes == 0) ||
+ (type & BTRFS_BLOCK_GROUP_RAID1 && num_stripes < 1) ||
+ (type & BTRFS_BLOCK_GROUP_RAID5 && num_stripes < 2) ||
+ (type & BTRFS_BLOCK_GROUP_RAID6 && num_stripes < 3) ||
+ (type & BTRFS_BLOCK_GROUP_DUP && num_stripes > 2) ||
+ ((type & BTRFS_BLOCK_GROUP_PROFILE_MASK) == 0 &&
+ num_stripes != 1)) {
+ error("Invalid num_stripes:sub_stripes %u:%u for profile %llu",
+ num_stripes, sub_stripes,
+ type & BTRFS_BLOCK_GROUP_PROFILE_MASK);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/*
+ * Slot is used to verify the chunk item is valid
*
* For sys chunk in superblock, pass -1 to indicate sys chunk.
*/
@@ -1600,7 +1707,6 @@ 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,32 +1715,14 @@ 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;
+ ret = btrfs_check_chunk_valid(root, leaf, chunk, slot, logical);
+ if (ret) {
+ error("%s checksums match, but it has an invalid chunk, %s",
+ (slot == -1) ? "Superblock" : "Metadata",
+ (slot == -1) ? "try btrfsck --repair -s <superblock> ie, 0,1,2" : "");
+ return ret;
}
ce = search_cache_extent(&map_tree->cache_tree, logical);
@@ -1658,50 +1746,6 @@ static int read_one_chunk(struct btrfs_root *root, struct btrfs_key *key,
map->type = btrfs_chunk_type(leaf, chunk);
map->sub_stripes = btrfs_chunk_sub_stripes(leaf, chunk);
- /* Check on chunk item type */
- if (map->type & ~(BTRFS_BLOCK_GROUP_TYPE_MASK |
- BTRFS_BLOCK_GROUP_PROFILE_MASK)) {
- fprintf(stderr, "Unknown chunk type bits: %llu\n",
- map->type & ~(BTRFS_BLOCK_GROUP_TYPE_MASK |
- BTRFS_BLOCK_GROUP_PROFILE_MASK));
- ret = -EIO;
- goto out;
- }
-
- /*
- * Btrfs_chunk contains at least one stripe, and for sys_chunk
- * it can't exceed the system chunk array size
- * For normal chunk, it should match its chunk item size.
- */
- if (num_stripes < 1 ||
- (slot == -1 && sizeof(struct btrfs_stripe) * num_stripes >
- BTRFS_SYSTEM_CHUNK_ARRAY_SIZE) ||
- (slot >= 0 && sizeof(struct btrfs_stripe) * (num_stripes - 1) >
- btrfs_item_size_nr(leaf, slot))) {
- fprintf(stderr, "Invalid num_stripes: %u\n",
- num_stripes);
- ret = -EIO;
- goto out;
- }
-
- /*
- * Device number check against profile
- */
- if ((map->type & BTRFS_BLOCK_GROUP_RAID10 && map->sub_stripes == 0) ||
- (map->type & BTRFS_BLOCK_GROUP_RAID1 && num_stripes < 1) ||
- (map->type & BTRFS_BLOCK_GROUP_RAID5 && num_stripes < 2) ||
- (map->type & BTRFS_BLOCK_GROUP_RAID6 && num_stripes < 3) ||
- (map->type & BTRFS_BLOCK_GROUP_DUP && num_stripes > 2) ||
- ((map->type & BTRFS_BLOCK_GROUP_PROFILE_MASK) == 0 &&
- num_stripes != 1)) {
- fprintf(stderr,
- "Invalid num_stripes:sub_stripes %u:%u for profile %llu\n",
- num_stripes, map->sub_stripes,
- map->type & BTRFS_BLOCK_GROUP_PROFILE_MASK);
- ret = -EIO;
- goto out;
- }
-
for (i = 0; i < num_stripes; i++) {
map->stripes[i].physical =
btrfs_stripe_offset_nr(leaf, chunk, i);
@@ -1722,9 +1766,6 @@ static int read_one_chunk(struct btrfs_root *root, struct btrfs_key *key,
BUG_ON(ret);
return 0;
-out:
- free(map);
- return ret;
}
static int fill_device_from_item(struct extent_buffer *leaf,
diff --git a/volumes.h b/volumes.h
index c0007ad..d88e1cf 100644
--- a/volumes.h
+++ b/volumes.h
@@ -194,7 +194,7 @@ int btrfs_alloc_chunk(struct btrfs_trans_handle *trans,
u64 *num_bytes, u64 type);
int btrfs_alloc_data_chunk(struct btrfs_trans_handle *trans,
struct btrfs_root *extent_root, u64 *start,
- u64 num_bytes, u64 type);
+ u64 num_bytes, u64 type, int convert);
int btrfs_read_super_device(struct btrfs_root *root, struct extent_buffer *buf);
int btrfs_add_device(struct btrfs_trans_handle *trans,
struct btrfs_root *root,