summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDimitri John Ledkov <xnox@ubuntu.com>2018-05-08 14:17:29 -0700
committerDimitri John Ledkov <xnox@ubuntu.com>2018-05-08 14:17:29 -0700
commitd00c9550da1801a0eaff5cedf4312e24691b31ea (patch)
tree3881ca1764ef792259e1b70f12c884a3ac0c0715
parentdab6d2181f1f194ec3a76d900cf2c6533379cbea (diff)
New upstream release.
-rw-r--r--.gitignore9
-rw-r--r--.travis.yml7
-rw-r--r--CHANGES43
-rw-r--r--Documentation/Makefile.in3
-rw-r--r--Documentation/btrfs-balance.8.gzbin6108 -> 6104 bytes
-rw-r--r--Documentation/btrfs-balance.asciidoc2
-rw-r--r--Documentation/btrfs-check.8.gzbin2726 -> 2725 bytes
-rw-r--r--Documentation/btrfs-check.asciidoc2
-rw-r--r--Documentation/btrfs-convert.8.gzbin2746 -> 2746 bytes
l---------Documentation/btrfs-debug-tree.8.gz1
-rw-r--r--Documentation/btrfs-device.8.gzbin4832 -> 4832 bytes
-rw-r--r--Documentation/btrfs-device.asciidoc4
-rw-r--r--Documentation/btrfs-filesystem.8.gzbin5879 -> 5878 bytes
-rw-r--r--Documentation/btrfs-filesystem.asciidoc2
-rw-r--r--Documentation/btrfs-find-root.8.gzbin863 -> 862 bytes
-rw-r--r--Documentation/btrfs-image.8.gzbin1464 -> 1464 bytes
-rw-r--r--Documentation/btrfs-inspect-internal.8.gzbin3037 -> 3078 bytes
-rw-r--r--Documentation/btrfs-inspect-internal.asciidoc2
-rw-r--r--Documentation/btrfs-man5.asciidoc48
-rw-r--r--Documentation/btrfs-map-logical.8.gzbin875 -> 875 bytes
-rw-r--r--Documentation/btrfs-property.8.gzbin1519 -> 1519 bytes
-rw-r--r--Documentation/btrfs-qgroup.8.gzbin2377 -> 2377 bytes
-rw-r--r--Documentation/btrfs-quota.8.gzbin4891 -> 4891 bytes
-rw-r--r--Documentation/btrfs-receive.8.gzbin2026 -> 2027 bytes
-rw-r--r--Documentation/btrfs-replace.8.gzbin1634 -> 1634 bytes
-rw-r--r--Documentation/btrfs-rescue.8.gzbin2047 -> 2047 bytes
-rw-r--r--Documentation/btrfs-restore.8.gzbin1980 -> 1979 bytes
-rw-r--r--Documentation/btrfs-scrub.8.gzbin2158 -> 2157 bytes
-rw-r--r--Documentation/btrfs-select-super.8.gzbin1267 -> 1248 bytes
-rw-r--r--Documentation/btrfs-select-super.asciidoc3
-rw-r--r--Documentation/btrfs-send.8.gzbin1719 -> 1719 bytes
l---------Documentation/btrfs-show-super.8.gz1
-rw-r--r--Documentation/btrfs-subvolume.8.gzbin3226 -> 3225 bytes
l---------Documentation/btrfs-zero-log.8.gz1
-rw-r--r--Documentation/btrfs.5.gzbin11006 -> 11324 bytes
-rw-r--r--Documentation/btrfs.8.gzbin2228 -> 2257 bytes
-rw-r--r--Documentation/btrfs.asciidoc8
-rw-r--r--Documentation/btrfstune.8.gzbin2051 -> 2052 bytes
-rw-r--r--Documentation/fsck.btrfs.8.gzbin1128 -> 1129 bytes
-rw-r--r--Documentation/mkfs.btrfs.8.gzbin6542 -> 6542 bytes
-rw-r--r--INSTALL4
-rw-r--r--Makefile133
-rw-r--r--Makefile.inc.in6
-rw-r--r--VERSION2
-rw-r--r--btrfs-calc-size.c39
-rw-r--r--btrfs-corrupt-block.c2
-rw-r--r--btrfs-debug-tree.c41
-rw-r--r--btrfs-sb-mod.c337
-rw-r--r--btrfs-show-super.c39
-rw-r--r--btrfs-zero-log.c78
-rw-r--r--check/main.c73
-rw-r--r--check/mode-common.c256
-rw-r--r--check/mode-common.h2
-rw-r--r--check/mode-lowmem.c83
-rw-r--r--check/mode-original.h1
-rw-r--r--cmds-fi-du.c2
-rw-r--r--cmds-filesystem.c21
-rw-r--r--cmds-inspect-dump-super.c5
-rw-r--r--cmds-inspect-dump-tree.c48
-rw-r--r--cmds-qgroup.c21
-rw-r--r--cmds-restore.c9
-rw-r--r--cmds-scrub.c2
-rw-r--r--cmds-subvolume.c533
-rw-r--r--config.h.in3
-rwxr-xr-xconfigure462
-rw-r--r--configure.ac32
-rw-r--r--convert/common.c4
-rw-r--r--convert/main.c6
-rw-r--r--convert/source-ext2.c5
-rw-r--r--convert/source-reiserfs.c6
-rw-r--r--ctree.c82
-rw-r--r--ctree.h26
-rw-r--r--debian/changelog6
-rw-r--r--disk-io.c21
-rw-r--r--disk-io.h10
-rw-r--r--extent-tree.c8
-rw-r--r--extent_io.c17
-rw-r--r--extent_io.h5
-rw-r--r--free-space-cache.c35
-rw-r--r--help.c2
-rw-r--r--help.h3
-rw-r--r--image/main.c6
-rw-r--r--libbtrfsutil/COPYING674
-rw-r--r--libbtrfsutil/COPYING.LESSER165
-rw-r--r--libbtrfsutil/README.md38
-rw-r--r--libbtrfsutil/btrfs.h840
-rw-r--r--libbtrfsutil/btrfs_tree.h970
-rw-r--r--libbtrfsutil/btrfsutil.h650
-rw-r--r--libbtrfsutil/btrfsutil_internal.h42
-rw-r--r--libbtrfsutil/errors.c55
-rw-r--r--libbtrfsutil/filesystem.c102
-rw-r--r--libbtrfsutil/python/.gitignore7
-rw-r--r--libbtrfsutil/python/btrfsutilpy.h84
-rw-r--r--libbtrfsutil/python/error.c202
-rw-r--r--libbtrfsutil/python/filesystem.c94
-rw-r--r--libbtrfsutil/python/module.c321
-rw-r--r--libbtrfsutil/python/qgroup.c141
-rwxr-xr-xlibbtrfsutil/python/setup.py111
-rw-r--r--libbtrfsutil/python/subvolume.c667
-rw-r--r--libbtrfsutil/python/tests/__init__.py70
-rw-r--r--libbtrfsutil/python/tests/test_filesystem.py73
-rw-r--r--libbtrfsutil/python/tests/test_qgroup.py57
-rw-r--r--libbtrfsutil/python/tests/test_subvolume.py385
-rw-r--r--libbtrfsutil/qgroup.c82
-rw-r--r--libbtrfsutil/stubs.c35
-rw-r--r--libbtrfsutil/stubs.h22
-rw-r--r--libbtrfsutil/subvolume.c1382
-rw-r--r--messages.h13
-rw-r--r--mkfs/common.c30
-rw-r--r--mkfs/main.c18
-rw-r--r--mkfs/rootdir.c17
-rw-r--r--print-tree.c65
-rw-r--r--print-tree.h4
-rw-r--r--props.c69
-rw-r--r--send-utils.c25
-rw-r--r--send-utils.h9
-rwxr-xr-xshow-blocks2
-rw-r--r--tests/README.md24
-rw-r--r--tests/common7
-rwxr-xr-xtests/convert-tests/014-reiserfs-tail-handling/test.sh5
-rwxr-xr-xtests/convert-tests/016-invalid-large-inline-extent/test.sh22
-rwxr-xr-xtests/fsck-tests/015-tree-reloc-tree/test.sh7
-rw-r--r--tests/fsck-tests/020-extent-ref-cases/extent_data_ref.imgbin0 -> 6144 bytes
-rwxr-xr-xtests/fsck-tests/020-extent-ref-cases/test.sh5
-rw-r--r--tests/fsck-tests/029-valid-orphan-item/orphan_inode.img.xzbin0 -> 1620 bytes
-rwxr-xr-xtests/fsck-tests/029-valid-orphan-item/test.sh15
-rw-r--r--tests/fsck-tests/030-reflinked-prealloc-extents/reflinked-prealloc-extents.img.xzbin0 -> 3244 bytes
-rwxr-xr-xtests/fsck-tests/030-reflinked-prealloc-extents/test.sh42
-rwxr-xr-xtests/fsck-tests/031-metadatadump-check-data-csum/test.sh30
-rwxr-xr-xtests/fuzz-tests/001-simple-check-unmounted/test.sh7
-rwxr-xr-xtests/fuzz-tests/002-simple-image/test.sh7
-rwxr-xr-xtests/fuzz-tests/003-multi-check-unmounted/test.sh2
-rwxr-xr-xtests/fuzz-tests/004-simple-dump-tree/test.sh7
-rwxr-xr-xtests/fuzz-tests/005-simple-dump-super/test.sh6
-rwxr-xr-xtests/fuzz-tests/006-simple-tree-stats/test.sh7
-rwxr-xr-xtests/fuzz-tests/008-simple-chunk-recover/test.sh4
-rwxr-xr-xtests/fuzz-tests/009-simple-zero-log/test.sh4
-rwxr-xr-xtests/misc-tests.sh1
-rwxr-xr-xtests/misc-tests/001-btrfstune-features/test.sh13
-rwxr-xr-xtests/misc-tests/002-uuid-rewrite/test.sh43
-rwxr-xr-xtests/misc-tests/003-zero-log/test.sh32
-rwxr-xr-xtests/misc-tests/004-shrink-fs/test.sh36
-rwxr-xr-xtests/misc-tests/005-convert-progress-thread-crash/test.sh6
-rwxr-xr-xtests/misc-tests/006-image-on-missing-device/test.sh22
-rwxr-xr-xtests/misc-tests/007-subvolume-sync/test.sh18
-rwxr-xr-xtests/misc-tests/008-leaf-crossing-stripes/test.sh4
-rwxr-xr-xtests/misc-tests/009-subvolume-sync-must-wait/test.sh28
-rwxr-xr-xtests/misc-tests/010-convert-delete-ext2-subvol/test.sh10
-rwxr-xr-xtests/misc-tests/011-delete-missing-device/test.sh22
-rwxr-xr-xtests/misc-tests/012-find-root-no-result/test.sh4
-rwxr-xr-xtests/misc-tests/013-subvolume-sync-crash/test.sh24
-rwxr-xr-xtests/misc-tests/014-filesystem-label/test.sh24
-rwxr-xr-xtests/misc-tests/015-dump-super-garbage/test.sh18
-rwxr-xr-xtests/misc-tests/016-send-clone-src/test.sh12
-rwxr-xr-xtests/misc-tests/018-recv-end-of-stream/test.sh68
-rwxr-xr-xtests/misc-tests/019-receive-clones-on-mounted-subvol/test.sh4
-rwxr-xr-xtests/misc-tests/021-image-multi-devices/test.sh22
-rwxr-xr-xtests/misc-tests/023-device-usage-with-missing-device/test.sh6
-rwxr-xr-xtests/misc-tests/024-inspect-internal-rootid/test.sh6
-rwxr-xr-xtests/misc-tests/028-superblock-recover/test.sh6
-rwxr-xr-xtests/misc-tests/029-send-p-different-mountpoints/test.sh51
-rwxr-xr-xtests/misc-tests/030-missing-device-image/test.sh58
-rwxr-xr-xtests/mkfs-tests/001-basic-profiles/test.sh19
-rwxr-xr-xtests/mkfs-tests/004-rootdir-keeps-size/test.sh12
-rwxr-xr-xtests/mkfs-tests/005-long-device-name-for-ssd/test.sh20
-rwxr-xr-xtests/mkfs-tests/006-partitioned-loopdev/test.sh10
-rwxr-xr-xtests/mkfs-tests/007-mix-nodesize-sectorsize/test.sh16
-rwxr-xr-xtests/mkfs-tests/008-sectorsize-nodesize-combination/test.sh10
-rwxr-xr-xtests/mkfs-tests/010-minimal-size/test.sh6
-rwxr-xr-xtests/mkfs-tests/011-rootdir-create-file/test.sh2
-rwxr-xr-xtests/mkfs-tests/012-rootdir-no-shrink/test.sh8
-rwxr-xr-xtests/mkfs-tests/013-reserved-1M-for-single/test.sh6
-rwxr-xr-xtests/mkfs-tests/014-rootdir-inline-extent/test.sh46
-rwxr-xr-xtests/mkfs-tests/015-fstree-uuid-otime/test.sh35
-rwxr-xr-xtests/mkfs-tests/016-rootdir-bad-symbolic-link/test.sh26
-rwxr-xr-xtravis/build-dep-reiserfs18
-rwxr-xr-xtravis/build-dep-zstd18
-rw-r--r--travis/images/ci-musl-x86_64/Dockerfile3
-rw-r--r--utils.c159
-rw-r--r--utils.h6
-rw-r--r--volumes.c97
-rw-r--r--volumes.h52
182 files changed, 10010 insertions, 1431 deletions
diff --git a/.gitignore b/.gitignore
index 4abd3ee5..144ebb3b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,11 +10,9 @@ Documentation/*.gz
Documentation/*.html
btrfs
btrfs.static
-btrfs-debug-tree
btrfs-map-logical
btrfs-fragments
btrfsck
-calc-size
ioctl-test
dir-test
send-test
@@ -28,11 +26,8 @@ btrfs-convert
btrfs-find-root
btrfs-find-root.static
btrfs-image
-btrfs-show-super
-btrfs-zero-log
btrfs-corrupt-block
btrfs-select-super
-btrfs-calc-size
btrfs-crc
btrfstune
mktables
@@ -42,6 +37,8 @@ libbtrfs.so.0
libbtrfs.so.0.1
library-test
library-test-static
+/libbtrfsutil.so*
+/libbtrfsutil.a
/fssum
testsuite-id
@@ -49,6 +46,7 @@ testsuite-id
/tests/test-console.txt
/tests/test.img
/tests/mnt/
+*.restored
aclocal.m4
autom4te.cache
@@ -79,3 +77,4 @@ stamp-h.in
stamp-h1
config/*
+*.patch
diff --git a/.travis.yml b/.travis.yml
index 6581fbd2..a9953bb3 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -25,6 +25,9 @@ compiler:
cache:
ccache: true
+ directories:
+ - tmp-cached-reiser
+ - tmp-cached-zstd
git:
depth: 1
@@ -50,7 +53,7 @@ env:
before_install:
- sudo apt-get update -qq
- - sudo apt-get install -qq e2fslibs-dev gcc libacl1-dev libblkid-dev liblzo2-dev make pkg-config udev zlib1g-dev acl attr reiserfsprogs
+ - sudo apt-get install -qq e2fslibs-dev gcc libacl1-dev libblkid-dev liblzo2-dev make pkg-config udev zlib1g-dev acl attr reiserfsprogs python3-setuptools
- echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca-
- docker pull kdave/ci-musl-x86_64
- travis/build-dep-reiserfs
@@ -69,7 +72,7 @@ addons:
script:
# quick build tests
- - "if travis/should-run-test; then docker run -it kdave/ci-musl-x86_64 ./test-build $TRAVIS_BRANCH --disable-documentation --disable-backtrace; fi"
+ - "if travis/should-run-test; then docker run -it --env CC=$CC kdave/ci-musl-x86_64 ./test-build $TRAVIS_BRANCH --disable-documentation --disable-backtrace; fi"
# real tests
- "if travis/should-run-test; then make TEST_LOG=dump test-cli; fi"
- "if travis/should-run-test; then make TEST_LOG=dump test-mkfs; fi"
diff --git a/CHANGES b/CHANGES
index 16016a33..5116d633 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,46 @@
+btrfs-progs-4.16.1 (2018-04-24)
+ * remove obsolete tools: btrfs-debug-tree, btrfs-zero-log, btrfs-show-super,
+ btrfs-calc-size
+ * sb-mod: new debugging tool to edit superblock items
+ * mkfs: detect if thin-provisioned device does not have enough space
+ * check: don't try to verify checksums on metadata dump images
+ * build: fail documentation build if xmlto is not found
+ * build: fix build of btrfs.static
+
+btrfs-progs-4.16 (2018-04-06)
+ * libbtrfsutil - new LGPL library to wrap userspace functionality
+ * several 'btrfs' commands converted to use it:
+ * properties
+ * filesystem sync
+ * subvolume set-default/get-default/delete/show/sync
+ * python bindings, tests
+ * build
+ * use configured pkg-config path
+ * CI: add python, musl/clang, built dependencies caching
+ * convert: build fix for e2fsprogs 1.44+
+ * don't install library links with wrong permissions
+ * fixes
+ * prevent incorrect use of subvol_strip_mountpoint
+ * dump-super: don't verify csum for unknown type
+ * convert: fix inline extent creation condition
+ * check:
+ * lowmem: fix false alert for 'data extent backref lost for snapshot'
+ * lowmem: fix false alert for orphan inode
+ * lowmem: fix false alert for shared prealloc extents
+ * mkfs:
+ * add UUID and otime to root of FS_TREE - with the uuid, snapshots will
+ be now linked to the toplevel subvol by the parent UUID
+ * don't follow symlinks when calculating size
+ * pre-create the UUID tree
+ * fix --rootdir with selinux enabled
+ * dump-tree: add option to print only children nodes of a given block
+ * image: handle missing device for RAID1
+ * other
+ * new tests
+ * test script cleanups (quoting, helpers)
+ * tool to edit superblocks
+ * updated docs
+
btrfs-progs-4.15.1 (2018-02-16)
* build
* fix build on musl
diff --git a/Documentation/Makefile.in b/Documentation/Makefile.in
index 64947afb..184647c4 100644
--- a/Documentation/Makefile.in
+++ b/Documentation/Makefile.in
@@ -102,9 +102,6 @@ install-man: man
$(MV) $(DESTDIR)$(man5dir)/btrfs-man5.5.gz $(DESTDIR)$(man5dir)/btrfs.5.gz
$(INSTALL) -m 644 $(GZ_MAN8) $(DESTDIR)$(man8dir)
$(LN_S) -f btrfs-check.8.gz $(DESTDIR)$(man8dir)/btrfsck.8.gz
- $(LN_S) -f btrfs-rescue.8.gz $(DESTDIR)$(man8dir)/btrfs-zero-log.8.gz
- $(LN_S) -f btrfs-inspect-internal.8.gz $(DESTDIR)$(man8dir)/btrfs-debug-tree.8.gz
- $(LN_S) -f btrfs-inspect-internal.8.gz $(DESTDIR)$(man8dir)/btrfs-show-super.8.gz
uninstall:
cd $(DESTDIR)$(man8dir); rm -f btrfs-check.8.gz $(GZ_MAN8)
diff --git a/Documentation/btrfs-balance.8.gz b/Documentation/btrfs-balance.8.gz
index 4e7ff56e..f51c43d0 100644
--- a/Documentation/btrfs-balance.8.gz
+++ b/Documentation/btrfs-balance.8.gz
Binary files differ
diff --git a/Documentation/btrfs-balance.asciidoc b/Documentation/btrfs-balance.asciidoc
index 7017bed7..536243bc 100644
--- a/Documentation/btrfs-balance.asciidoc
+++ b/Documentation/btrfs-balance.asciidoc
@@ -204,7 +204,7 @@ The way balance operates, it usually needs to temporarily create a new block
group and move the old data there, before the old block group can be removed.
For that it needs the work space, otherwise it fails for ENOSPC reasons.
This is not the same ENOSPC as if the free space is exhausted. This refers to
-the space on the level of block groups, which are bigger parts of the filesytem
+the space on the level of block groups, which are bigger parts of the filesystem
that contain many file extents.
The free work space can be calculated from the output of the *btrfs filesystem show*
diff --git a/Documentation/btrfs-check.8.gz b/Documentation/btrfs-check.8.gz
index fde8c5fb..f811ae43 100644
--- a/Documentation/btrfs-check.8.gz
+++ b/Documentation/btrfs-check.8.gz
Binary files differ
diff --git a/Documentation/btrfs-check.asciidoc b/Documentation/btrfs-check.asciidoc
index cc76d846..b963eae5 100644
--- a/Documentation/btrfs-check.asciidoc
+++ b/Documentation/btrfs-check.asciidoc
@@ -122,7 +122,7 @@ NOTE: 'lowmem' mode does not work with '--repair' yet, and is still considered
experimental.
--force::
-allow to work on a mounted filesystem. Note that this should work fine on a
+allow work on a mounted filesystem. Note that this should work fine on a
quiescent or read-only mounted filesystem but may crash if the device is
changed externally, eg. by the kernel module. Repair without mount checks is
not supported right now.
diff --git a/Documentation/btrfs-convert.8.gz b/Documentation/btrfs-convert.8.gz
index 2a283976..85f9cf3e 100644
--- a/Documentation/btrfs-convert.8.gz
+++ b/Documentation/btrfs-convert.8.gz
Binary files differ
diff --git a/Documentation/btrfs-debug-tree.8.gz b/Documentation/btrfs-debug-tree.8.gz
deleted file mode 120000
index e69216e2..00000000
--- a/Documentation/btrfs-debug-tree.8.gz
+++ /dev/null
@@ -1 +0,0 @@
-btrfs-inspect-internal.8.gz \ No newline at end of file
diff --git a/Documentation/btrfs-device.8.gz b/Documentation/btrfs-device.8.gz
index d2f09280..3daf4dbc 100644
--- a/Documentation/btrfs-device.8.gz
+++ b/Documentation/btrfs-device.8.gz
Binary files differ
diff --git a/Documentation/btrfs-device.asciidoc b/Documentation/btrfs-device.asciidoc
index 223ade5c..04d31503 100644
--- a/Documentation/btrfs-device.asciidoc
+++ b/Documentation/btrfs-device.asciidoc
@@ -124,7 +124,7 @@ statistics and the meaning.
Print the stats and reset the values to zero afterwards.
-c|--check::::
-Check if the stats are all zeros and return 0 it this is so. Set bit 6 of the
+Check if the stats are all zeros and return 0 if it is so. Set bit 6 of the
return code if any of the statistics is no-zero. The error values is 65 if
reading stats from at least one device failed, otherwise it's 64.
@@ -190,7 +190,7 @@ there's enough workspace for the conversion process, we can do:
$ btrfs balance start -mconvert=raid1 /mnt
-This operation can take a while, because al metadata have to be moved and all
+This operation can take a while, because all 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.
diff --git a/Documentation/btrfs-filesystem.8.gz b/Documentation/btrfs-filesystem.8.gz
index 2955df08..91f00e79 100644
--- a/Documentation/btrfs-filesystem.8.gz
+++ b/Documentation/btrfs-filesystem.8.gz
Binary files differ
diff --git a/Documentation/btrfs-filesystem.asciidoc b/Documentation/btrfs-filesystem.asciidoc
index 13c2d7cc..f0a9ba4d 100644
--- a/Documentation/btrfs-filesystem.asciidoc
+++ b/Documentation/btrfs-filesystem.asciidoc
@@ -3,7 +3,7 @@ btrfs-filesystem(8)
NAME
----
-btrfs-filesystem - command group othat primarily does work on the whole filesystems
+btrfs-filesystem - command group that primarily does work on the whole filesystems
SYNOPSIS
--------
diff --git a/Documentation/btrfs-find-root.8.gz b/Documentation/btrfs-find-root.8.gz
index 11c03171..92fcc6b6 100644
--- a/Documentation/btrfs-find-root.8.gz
+++ b/Documentation/btrfs-find-root.8.gz
Binary files differ
diff --git a/Documentation/btrfs-image.8.gz b/Documentation/btrfs-image.8.gz
index be540124..b765137a 100644
--- a/Documentation/btrfs-image.8.gz
+++ b/Documentation/btrfs-image.8.gz
Binary files differ
diff --git a/Documentation/btrfs-inspect-internal.8.gz b/Documentation/btrfs-inspect-internal.8.gz
index 318ac56d..2fdc7275 100644
--- a/Documentation/btrfs-inspect-internal.8.gz
+++ b/Documentation/btrfs-inspect-internal.8.gz
Binary files differ
diff --git a/Documentation/btrfs-inspect-internal.asciidoc b/Documentation/btrfs-inspect-internal.asciidoc
index e072a943..e2db6466 100644
--- a/Documentation/btrfs-inspect-internal.asciidoc
+++ b/Documentation/btrfs-inspect-internal.asciidoc
@@ -87,6 +87,8 @@ the respective tree root block offset
print only the uuid tree information, empty output if the tree does not exist
-b <block_num>::::
print info of the specified block only
+--follow::::
+use with '-b', print all children tree blocks of '<block_num>'
-t <tree_id>::::
print only the tree with the specified ID, where the ID can be numerical or
common name in a flexible human readable form
diff --git a/Documentation/btrfs-man5.asciidoc b/Documentation/btrfs-man5.asciidoc
index 367736ce..0529496a 100644
--- a/Documentation/btrfs-man5.asciidoc
+++ b/Documentation/btrfs-man5.asciidoc
@@ -14,9 +14,11 @@ tools. Currently covers:
2. filesystem features
-3. file attributes
+3. filesystem limits
-4. control device
+4. file attributes
+
+5. control device
MOUNT OPTIONS
@@ -208,7 +210,7 @@ system at that point.
Enable discarding of freed file blocks. This is useful for SSD devices, thinly
provisioned LUNs, or virtual machine images; however, every storage layer must
support discard for it to work. if the backing device does not support
-asynchronous queued TRIM, then this operation can severly degrade performance,
+asynchronous queued TRIM, then this operation can severely degrade performance,
because a synchronous TRIM operation will be attempted instead. Queued TRIM
requires newer than SATA revision 3.1 chipsets and devices.
@@ -221,7 +223,7 @@ of actually discarding the blocks.
If discarding is not necessary to be done at the block freeing time, there's
`fstrim`(8) tool that lets the filesystem discard all free blocks in a batch,
-possibly not much interfering with other operations. Also, the the device may
+possibly not much interfering with other operations. Also, the device may
ignore the TRIM command if the range is too small, so running the batch discard
can actually discard the blocks.
@@ -287,7 +289,7 @@ checksums don't fit inside a single page.
+
Don't use this option unless you really need it. The inode number limit
on 64bit system is 2^64^, which is practically enough for the whole filesystem
-lifetime. Due to implemention of linux VFS layer, the inode numbers on 32bit
+lifetime. Due to implementation of linux VFS layer, the inode numbers on 32bit
systems are only 32 bits wide. This lowers the limit significantly and makes
it possible to reach it. In such case, this mount option will help.
Alternatively, files with high inode numbers can be copied to a new subvolume
@@ -413,7 +415,7 @@ will disable all SSD options.
*subvol='path'*::
Mount subvolume from 'path' rather than the toplevel subvolume. The
-'path' is always treated as relative to the the toplevel subvolume.
+'path' is always treated as relative to the toplevel subvolume.
This mount option overrides the default subvolume set for the given filesystem.
*subvolid='subvolid'*::
@@ -608,6 +610,40 @@ the filesystem contains or contained a raid56 profile of block groups
+
reduced-size metadata for extent references, saves a few percent of metadata
+
+FILESYSTEM LIMITS
+-----------------
+
+maximum file name length::
+255
+
+maximum symlink target length::
+depends on the 'nodesize' value, for 4k it's 3949 bytes, for larger nodesize
+it's 4095
++
+The symlink target may not be a valid path, ie the path name components
+can exceed the limits, there's no content validation at `symlink`(3) creation.
+
+maximum number of inodes::
+2^64^ but depends on the available metadata space as the inodes are created
+dynamically
+
+inode numbers::
+minimum number: 256 (for subvolumes), regular files and directories: 257
+
+maximum file length::
+inherent limit of btrfs is 2^64^ (16 EiB) but the linux VFS limit is 2^63^ (8 EiB)
+
+maximum number of subvolumes::
+2^64^ but depends on the available metadata space, the space consumed by all
+subvolume metadata includes bookeeping of the shared extents can be large (MiB,
+GiB)
+
+maximum number of hardlinks of a file in a directory::
+65536 when the `extref` feature is turned on during mkfs (default), roughly
+100 otherwise
+
+
FILE ATTRIBUTES
---------------
The btrfs filesystem supports setting the following file attributes using the
diff --git a/Documentation/btrfs-map-logical.8.gz b/Documentation/btrfs-map-logical.8.gz
index 2f9a9c12..00d0c048 100644
--- a/Documentation/btrfs-map-logical.8.gz
+++ b/Documentation/btrfs-map-logical.8.gz
Binary files differ
diff --git a/Documentation/btrfs-property.8.gz b/Documentation/btrfs-property.8.gz
index b05b0a6d..4a8101aa 100644
--- a/Documentation/btrfs-property.8.gz
+++ b/Documentation/btrfs-property.8.gz
Binary files differ
diff --git a/Documentation/btrfs-qgroup.8.gz b/Documentation/btrfs-qgroup.8.gz
index f76382ef..13feefed 100644
--- a/Documentation/btrfs-qgroup.8.gz
+++ b/Documentation/btrfs-qgroup.8.gz
Binary files differ
diff --git a/Documentation/btrfs-quota.8.gz b/Documentation/btrfs-quota.8.gz
index 45c9b665..c400008e 100644
--- a/Documentation/btrfs-quota.8.gz
+++ b/Documentation/btrfs-quota.8.gz
Binary files differ
diff --git a/Documentation/btrfs-receive.8.gz b/Documentation/btrfs-receive.8.gz
index f0d644e8..5b782a3d 100644
--- a/Documentation/btrfs-receive.8.gz
+++ b/Documentation/btrfs-receive.8.gz
Binary files differ
diff --git a/Documentation/btrfs-replace.8.gz b/Documentation/btrfs-replace.8.gz
index fe411c4f..ca3cb8ff 100644
--- a/Documentation/btrfs-replace.8.gz
+++ b/Documentation/btrfs-replace.8.gz
Binary files differ
diff --git a/Documentation/btrfs-rescue.8.gz b/Documentation/btrfs-rescue.8.gz
index 061456ac..681dae38 100644
--- a/Documentation/btrfs-rescue.8.gz
+++ b/Documentation/btrfs-rescue.8.gz
Binary files differ
diff --git a/Documentation/btrfs-restore.8.gz b/Documentation/btrfs-restore.8.gz
index 75181ba5..3a7371ce 100644
--- a/Documentation/btrfs-restore.8.gz
+++ b/Documentation/btrfs-restore.8.gz
Binary files differ
diff --git a/Documentation/btrfs-scrub.8.gz b/Documentation/btrfs-scrub.8.gz
index 331c1a9b..8aa92d8f 100644
--- a/Documentation/btrfs-scrub.8.gz
+++ b/Documentation/btrfs-scrub.8.gz
Binary files differ
diff --git a/Documentation/btrfs-select-super.8.gz b/Documentation/btrfs-select-super.8.gz
index d1bd2502..d0afda32 100644
--- a/Documentation/btrfs-select-super.8.gz
+++ b/Documentation/btrfs-select-super.8.gz
Binary files differ
diff --git a/Documentation/btrfs-select-super.asciidoc b/Documentation/btrfs-select-super.asciidoc
index 6e94a034..e3bca98b 100644
--- a/Documentation/btrfs-select-super.asciidoc
+++ b/Documentation/btrfs-select-super.asciidoc
@@ -21,8 +21,7 @@ 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*.
+To dump a superblock use the *btrfs inspect-internal dump-super* command.
Then run the check (in the non-repair mode) using the command *btrfs check -s*
where '-s' specifies the superblock copy to use.
diff --git a/Documentation/btrfs-send.8.gz b/Documentation/btrfs-send.8.gz
index 808a4bb2..0f31ed82 100644
--- a/Documentation/btrfs-send.8.gz
+++ b/Documentation/btrfs-send.8.gz
Binary files differ
diff --git a/Documentation/btrfs-show-super.8.gz b/Documentation/btrfs-show-super.8.gz
deleted file mode 120000
index e69216e2..00000000
--- a/Documentation/btrfs-show-super.8.gz
+++ /dev/null
@@ -1 +0,0 @@
-btrfs-inspect-internal.8.gz \ No newline at end of file
diff --git a/Documentation/btrfs-subvolume.8.gz b/Documentation/btrfs-subvolume.8.gz
index ba8ff190..90e7a138 100644
--- a/Documentation/btrfs-subvolume.8.gz
+++ b/Documentation/btrfs-subvolume.8.gz
Binary files differ
diff --git a/Documentation/btrfs-zero-log.8.gz b/Documentation/btrfs-zero-log.8.gz
deleted file mode 120000
index 7c007012..00000000
--- a/Documentation/btrfs-zero-log.8.gz
+++ /dev/null
@@ -1 +0,0 @@
-btrfs-rescue.8.gz \ No newline at end of file
diff --git a/Documentation/btrfs.5.gz b/Documentation/btrfs.5.gz
index c33db2bb..9ead78aa 100644
--- a/Documentation/btrfs.5.gz
+++ b/Documentation/btrfs.5.gz
Binary files differ
diff --git a/Documentation/btrfs.8.gz b/Documentation/btrfs.8.gz
index 1ad76a10..b7584036 100644
--- a/Documentation/btrfs.8.gz
+++ b/Documentation/btrfs.8.gz
Binary files differ
diff --git a/Documentation/btrfs.asciidoc b/Documentation/btrfs.asciidoc
index 82530234..7316ac09 100644
--- a/Documentation/btrfs.asciidoc
+++ b/Documentation/btrfs.asciidoc
@@ -120,9 +120,11 @@ Tools that are still in active use without an equivalent in *btrfs*:
Deprecated and obsolete tools:
-*btrfs-debug-tree*:: moved to *btrfs inspect-internal dump-tree*
-*btrfs-show-super*:: moved to *btrfs inspect-internal dump-super*
-*btrfs-zero-log*:: moved to *btrfs rescue zero-log*
+*btrfs-debug-tree*:: moved to *btrfs inspect-internal dump-tree*. Removed from
+source distribution.
+*btrfs-show-super*:: moved to *btrfs inspect-internal dump-super*, standalone
+removed.
+*btrfs-zero-log*:: moved to *btrfs rescue zero-log*, standalone removed.
EXIT STATUS
-----------
diff --git a/Documentation/btrfstune.8.gz b/Documentation/btrfstune.8.gz
index 2a4babfc..9c403588 100644
--- a/Documentation/btrfstune.8.gz
+++ b/Documentation/btrfstune.8.gz
Binary files differ
diff --git a/Documentation/fsck.btrfs.8.gz b/Documentation/fsck.btrfs.8.gz
index e47e730c..997d4db8 100644
--- a/Documentation/fsck.btrfs.8.gz
+++ b/Documentation/fsck.btrfs.8.gz
Binary files differ
diff --git a/Documentation/mkfs.btrfs.8.gz b/Documentation/mkfs.btrfs.8.gz
index 36275e8b..b0b86e15 100644
--- a/Documentation/mkfs.btrfs.8.gz
+++ b/Documentation/mkfs.btrfs.8.gz
Binary files differ
diff --git a/INSTALL b/INSTALL
index 819b92ea..24d6e24f 100644
--- a/INSTALL
+++ b/INSTALL
@@ -41,6 +41,10 @@ To build from the released tarballs:
$ make
$ make install
+To install the libbtrfsutil Python bindings:
+
+ $ make install_python
+
You may disable building some parts like documentation, btrfs-convert or
backtrace support. See ./configure --help for more.
diff --git a/Makefile b/Makefile
index 92cfe7b5..cbd85533 100644
--- a/Makefile
+++ b/Makefile
@@ -73,10 +73,20 @@ CFLAGS = $(SUBST_CFLAGS) \
-fPIC \
-I$(TOPDIR) \
-I$(TOPDIR)/kernel-lib \
+ -I$(TOPDIR)/libbtrfsutil \
$(EXTRAWARN_CFLAGS) \
$(DEBUG_CFLAGS_INTERNAL) \
$(EXTRA_CFLAGS)
+LIBBTRFSUTIL_CFLAGS = $(SUBST_CFLAGS) \
+ $(CSTD) \
+ -D_GNU_SOURCE \
+ -fPIC \
+ -fvisibility=hidden \
+ -I$(TOPDIR)/libbtrfsutil \
+ $(EXTRAWARN_CFLAGS) \
+ $(EXTRA_CFLAGS)
+
LDFLAGS = $(SUBST_LDFLAGS) \
-rdynamic -L$(TOPDIR) \
$(DEBUG_LDFLAGS_INTERNAL) \
@@ -121,12 +131,19 @@ libbtrfs_headers = send-stream.h send-utils.h send.h kernel-lib/rbtree.h btrfs-l
kernel-lib/crc32c.h kernel-lib/list.h kerncompat.h \
kernel-lib/radix-tree.h kernel-lib/sizes.h kernel-lib/raid56.h \
extent-cache.h extent_io.h ioctl.h ctree.h btrfsck.h version.h
+libbtrfsutil_major := $(shell sed -rn 's/^\#define BTRFS_UTIL_VERSION_MAJOR ([0-9])+$$/\1/p' libbtrfsutil/btrfsutil.h)
+libbtrfsutil_minor := $(shell sed -rn 's/^\#define BTRFS_UTIL_VERSION_MINOR ([0-9])+$$/\1/p' libbtrfsutil/btrfsutil.h)
+libbtrfsutil_patch := $(shell sed -rn 's/^\#define BTRFS_UTIL_VERSION_PATCH ([0-9])+$$/\1/p' libbtrfsutil/btrfsutil.h)
+libbtrfsutil_version := $(libbtrfsutil_major).$(libbtrfsutil_minor).$(libbtrfsutil_patch)
+libbtrfsutil_objects = libbtrfsutil/errors.o libbtrfsutil/filesystem.o \
+ libbtrfsutil/subvolume.o libbtrfsutil/qgroup.o \
+ libbtrfsutil/stubs.o
convert_objects = convert/main.o convert/common.o convert/source-fs.o \
convert/source-ext2.o convert/source-reiserfs.o
mkfs_objects = mkfs/main.o mkfs/common.o mkfs/rootdir.o
image_objects = image/main.o image/sanitize.o
all_objects = $(objects) $(cmds_objects) $(libbtrfs_objects) $(convert_objects) \
- $(mkfs_objects) $(image_objects)
+ $(mkfs_objects) $(image_objects) $(libbtrfsutil_objects)
udev_rules = 64-btrfs-dm.rules
@@ -139,8 +156,10 @@ endif
ifeq ($(BUILD_VERBOSE),1)
Q =
+ SETUP_PY_Q =
else
Q = @
+ SETUP_PY_Q = -q
endif
ifeq ("$(origin D)", "command line")
@@ -191,13 +210,12 @@ MAKEOPTS = --no-print-directory Q=$(Q)
progs = $(progs_install) btrfsck btrfs-corrupt-block
# install only selected
-progs_install = btrfs mkfs.btrfs btrfs-debug-tree \
- btrfs-map-logical btrfs-image btrfs-zero-log \
+progs_install = btrfs mkfs.btrfs btrfs-map-logical btrfs-image \
btrfs-find-root btrfstune \
btrfs-select-super
# other tools, not built by default
-progs_extra = btrfs-fragments btrfs-calc-size btrfs-show-super
+progs_extra = btrfs-fragments
progs_static = $(foreach p,$(progs),$(p).static)
@@ -210,9 +228,6 @@ endif
btrfs_convert_cflags = -DBTRFSCONVERT_EXT2=$(BTRFSCONVERT_EXT2)
btrfs_convert_cflags += -DBTRFSCONVERT_REISERFS=$(BTRFSCONVERT_REISERFS)
btrfs_fragments_libs = -lgd -lpng -ljpeg -lfreetype
-btrfs_debug_tree_objects = cmds-inspect-dump-tree.o
-btrfs_show_super_objects = cmds-inspect-dump-super.o
-btrfs_calc_size_objects = cmds-inspect-tree-stats.o
cmds_restore_cflags = -DBTRFSRESTORE_ZSTD=$(BTRFSRESTORE_ZSTD)
CHECKER_FLAGS += $(btrfs_convert_cflags)
@@ -242,15 +257,15 @@ endif
static_objects = $(patsubst %.o, %.static.o, $(objects))
static_cmds_objects = $(patsubst %.o, %.static.o, $(cmds_objects))
static_libbtrfs_objects = $(patsubst %.o, %.static.o, $(libbtrfs_objects))
+static_libbtrfsutil_objects = $(patsubst %.o, %.static.o, $(libbtrfsutil_objects))
static_convert_objects = $(patsubst %.o, %.static.o, $(convert_objects))
static_mkfs_objects = $(patsubst %.o, %.static.o, $(mkfs_objects))
static_image_objects = $(patsubst %.o, %.static.o, $(image_objects))
-libs_shared = libbtrfs.so.0.1
-libs_static = libbtrfs.a
+libs_shared = libbtrfs.so.0.1 libbtrfsutil.so.$(libbtrfsutil_version)
+libs_static = libbtrfs.a libbtrfsutil.a
libs = $(libs_shared) $(libs_static)
-lib_links = libbtrfs.so.0 libbtrfs.so
-headers = $(libbtrfs_headers)
+lib_links = libbtrfs.so.0 libbtrfs.so libbtrfsutil.so.$(libbtrfsutil_major) libbtrfsutil.so
# make C=1 to enable sparse
ifdef C
@@ -287,7 +302,10 @@ endif
$(Q)$(CC) $(STATIC_CFLAGS) -c $< -o $@ $($(subst -,_,$(@:%.static.o=%)-cflags)) \
$($(subst -,_,btrfs-$(@:%/$(notdir $@)=%)-cflags))
-all: $(progs) libbtrfs $(BUILDDIRS)
+all: $(progs) $(libs) $(lib_links) $(BUILDDIRS)
+ifeq ($(PYTHON_BINDINGS),1)
+all: libbtrfsutil_python
+endif
$(SUBDIRS): $(BUILDDIRS)
$(BUILDDIRS):
@echo "Making all in $(patsubst build-%,%,$@)"
@@ -303,7 +321,7 @@ test-fsck: btrfs btrfs-image btrfs-corrupt-block mkfs.btrfs btrfstune
$(Q)bash tests/fsck-tests.sh
test-misc: btrfs btrfs-image btrfs-corrupt-block mkfs.btrfs btrfstune fssum \
- btrfs-zero-log btrfs-find-root btrfs-select-super btrfs-convert
+ btrfs-find-root btrfs-select-super btrfs-convert
@echo " [TEST] misc-tests.sh"
$(Q)bash tests/misc-tests.sh
@@ -335,6 +353,16 @@ testsuite: btrfs-corrupt-block fssum
@echo "Export tests as a package"
$(Q)cd tests && ./export-testsuite.sh
+ifeq ($(PYTHON_BINDINGS),1)
+test-libbtrfsutil: libbtrfsutil_python mkfs.btrfs
+ $(Q)cd libbtrfsutil/python; \
+ LD_LIBRARY_PATH=../.. $(PYTHON) -m unittest discover -v tests
+
+.PHONY: test-libbtrfsutil
+
+test: test-libbtrfsutil
+endif
+
#
# NOTE: For static compiles, you need to have all the required libs
# static equivalent available
@@ -355,20 +383,44 @@ kernel-lib/tables.c:
@echo " [TABLE] $@"
$(Q)./mktables > $@ || ($(RM) -f $@ && exit 1)
-libbtrfs: $(libs_shared) $(lib_links)
+libbtrfs.so.0.1: $(libbtrfs_objects)
+ @echo " [LD] $@"
+ $(Q)$(CC) $(CFLAGS) $^ $(LDFLAGS) $(LIBBTRFS_LIBS) \
+ -shared -Wl,-soname,libbtrfs.so.0 -o $@
+
+libbtrfs.a: $(libbtrfs_objects)
+ @echo " [AR] $@"
+ $(Q)$(AR) cr $@ $^
+
+libbtrfs.so.0 libbtrfs.so: libbtrfs.so.0.1
+ @echo " [LN] $@"
+ $(Q)$(LN_S) -f $< $@
+
+libbtrfsutil/%.o: libbtrfsutil/%.c
+ @echo " [CC] $@"
+ $(Q)$(CC) $(LIBBTRFSUTIL_CFLAGS) -o $@ -c $< -o $@
-$(libs_shared): $(libbtrfs_objects) $(lib_links) send.h
+libbtrfsutil.so.$(libbtrfsutil_version): $(libbtrfsutil_objects)
@echo " [LD] $@"
- $(Q)$(CC) $(CFLAGS) $(libbtrfs_objects) $(LDFLAGS) $(LIBBTRFS_LIBS) \
- -shared -Wl,-soname,libbtrfs.so.0 -o libbtrfs.so.0.1
+ $(Q)$(CC) $(LIBBTRFSUTIL_CFLAGS) $(libbtrfsutil_objects) \
+ -shared -Wl,-soname,libbtrfsutil.so.$(libbtrfsutil_major) -o $@
-$(libs_static): $(libbtrfs_objects)
+libbtrfsutil.a: $(libbtrfsutil_objects)
@echo " [AR] $@"
- $(Q)$(AR) cr libbtrfs.a $(libbtrfs_objects)
+ $(Q)$(AR) cr $@ $^
-$(lib_links):
+libbtrfsutil.so.$(libbtrfsutil_major) libbtrfsutil.so: libbtrfsutil.so.$(libbtrfsutil_version)
@echo " [LN] $@"
- $(Q)$(LN_S) -f libbtrfs.so.0.1 $@
+ $(Q)$(LN_S) -f $< $@
+
+ifeq ($(PYTHON_BINDINGS),1)
+libbtrfsutil_python: libbtrfsutil.so.$(libbtrfsutil_major) libbtrfsutil.so libbtrfsutil/btrfsutil.h
+ @echo " [PY] libbtrfsutil"
+ $(Q)cd libbtrfsutil/python; \
+ CFLAGS= LDFLAGS= $(PYTHON) setup.py $(SETUP_PY_Q) build_ext -i build
+
+.PHONY: libbtrfsutil_python
+endif
# keep intermediate files from the below implicit rules around
.PRECIOUS: $(addsuffix .o,$(progs))
@@ -398,7 +450,7 @@ btrfs: btrfs.o $(objects) $(cmds_objects) $(libs_static)
@echo " [LD] $@"
$(Q)$(CC) -o $@ $^ $(LDFLAGS) $(LIBS) $(LIBS_COMP)
-btrfs.static: btrfs.static.o $(static_objects) $(static_cmds_objects) $(static_libbtrfs_objects)
+btrfs.static: btrfs.static.o $(static_objects) $(static_cmds_objects) $(static_libbtrfs_objects) $(static_libbtrfsutil_objects)
@echo " [LD] $@"
$(Q)$(CC) -o $@ $^ $(STATIC_LDFLAGS) $(STATIC_LIBS) $(STATIC_LIBS_COMP)
@@ -487,7 +539,7 @@ test-ioctl: ioctl-test ioctl-test-32 ioctl-test-64
$(Q)./ioctl-test-32 > ioctl-test-32.log
$(Q)./ioctl-test-64 > ioctl-test-64.log
-library-test: library-test.c $(libs_shared)
+library-test: library-test.c libbtrfs.so
@echo " [TEST PREP] $@"$(eval TMPD=$(shell mktemp -d))
$(Q)mkdir -p $(TMPD)/include/btrfs && \
cp $(libbtrfs_headers) $(TMPD)/include/btrfs && \
@@ -530,11 +582,15 @@ manpages:
tags: FORCE
@echo " [TAGS] $(TAGS_CMD)"
- $(Q)$(TAGS_CMD) *.[ch] image/*.[ch] convert/*.[ch] mkfs/*.[ch]
+ $(Q)$(TAGS_CMD) *.[ch] image/*.[ch] convert/*.[ch] mkfs/*.[ch] \
+ check/*.[ch] kernel-lib/*.[ch] kernel-shared/*.[ch] \
+ libbtrfsutil/*.[ch]
cscope: FORCE
@echo " [CSCOPE] $(CSCOPE_CMD)"
- $(Q)ls -1 *.[ch] image/*.[ch] convert/*.[ch] mkfs/*.[ch] > cscope.files
+ $(Q)ls -1 *.[ch] image/*.[ch] convert/*.[ch] mkfs/*.[ch] check/*.[ch] \
+ kernel-lib/*.[ch] kernel-shared/*.[ch] libbtrfsutil/*.[ch] \
+ > cscope.files
$(Q)$(CSCOPE_CMD)
clean-all: clean clean-doc clean-gen
@@ -551,7 +607,12 @@ clean: $(CLEANDIRS)
mktables btrfs.static mkfs.btrfs.static fssum \
$(check_defs) \
$(libs) $(lib_links) \
- $(progs_static) $(progs_extra)
+ $(progs_static) $(progs_extra) \
+ libbtrfsutil/*.o libbtrfsutil/*.o.d
+ifeq ($(PYTHON_BINDINGS),1)
+ $(Q)cd libbtrfsutil/python; \
+ $(PYTHON) setup.py $(SETUP_PY_Q) clean -a
+endif
clean-doc:
@echo "Cleaning Documentation"
@@ -578,14 +639,23 @@ install: $(libs) $(progs_install) $(INSTALLDIRS)
$(LN_S) -f btrfs $(DESTDIR)$(bindir)/btrfsck
$(INSTALL) -m755 -d $(DESTDIR)$(libdir)
$(INSTALL) $(libs) $(DESTDIR)$(libdir)
- cp -a $(lib_links) $(DESTDIR)$(libdir)
- $(INSTALL) -m755 -d $(DESTDIR)$(incdir)
- $(INSTALL) -m644 $(headers) $(DESTDIR)$(incdir)
+ cp -d $(lib_links) $(DESTDIR)$(libdir)
+ $(INSTALL) -m755 -d $(DESTDIR)$(incdir)/btrfs
+ $(INSTALL) -m644 $(libbtrfs_headers) $(DESTDIR)$(incdir)/btrfs
+ $(INSTALL) -m644 libbtrfsutil/btrfsutil.h $(DESTDIR)$(incdir)
ifneq ($(udevdir),)
$(INSTALL) -m755 -d $(DESTDIR)$(udevruledir)
$(INSTALL) -m644 $(udev_rules) $(DESTDIR)$(udevruledir)
endif
+ifeq ($(PYTHON_BINDINGS),1)
+install_python: libbtrfsutil_python
+ $(Q)cd libbtrfsutil/python; \
+ $(PYTHON) setup.py install --skip-build $(if $(DESTDIR),--root $(DESTDIR)) --prefix $(prefix)
+
+.PHONY: install_python
+endif
+
install-static: $(progs_static) $(INSTALLDIRS)
$(INSTALL) -m755 -d $(DESTDIR)$(bindir)
$(INSTALL) $(progs_static) $(DESTDIR)$(bindir)
@@ -598,8 +668,9 @@ $(INSTALLDIRS):
uninstall:
$(Q)$(MAKE) $(MAKEOPTS) -C Documentation uninstall
- cd $(DESTDIR)$(incdir); $(RM) -f -- $(headers)
- $(RMDIR) -p --ignore-fail-on-non-empty -- $(DESTDIR)$(incdir)
+ cd $(DESTDIR)$(incdir)/btrfs; $(RM) -f -- $(libbtrfs_headers)
+ $(RMDIR) -p --ignore-fail-on-non-empty -- $(DESTDIR)$(incdir)/btrfs
+ cd $(DESTDIR)$(incdir); $(RM) -f -- btrfsutil.h
cd $(DESTDIR)$(libdir); $(RM) -f -- $(lib_links) $(libs)
cd $(DESTDIR)$(bindir); $(RM) -f -- btrfsck fsck.btrfs $(progs_install)
diff --git a/Makefile.inc.in b/Makefile.inc.in
index 56271903..52410f69 100644
--- a/Makefile.inc.in
+++ b/Makefile.inc.in
@@ -14,12 +14,16 @@ DISABLE_BTRFSCONVERT = @DISABLE_BTRFSCONVERT@
BTRFSCONVERT_EXT2 = @BTRFSCONVERT_EXT2@
BTRFSCONVERT_REISERFS = @BTRFSCONVERT_REISERFS@
BTRFSRESTORE_ZSTD = @BTRFSRESTORE_ZSTD@
+PYTHON_BINDINGS = @PYTHON_BINDINGS@
+PYTHON = @PYTHON@
+PYTHON_CFLAGS = @PYTHON_CFLAGS@
SUBST_CFLAGS = @CFLAGS@
SUBST_LDFLAGS = @LDFLAGS@
LIBS_BASE = @UUID_LIBS@ @BLKID_LIBS@ -L. -pthread
LIBS_COMP = @ZLIB_LIBS@ @LZO2_LIBS@ @ZSTD_LIBS@
+LIBS_PYTHON = @PYTHON_LIBS@
STATIC_LIBS_BASE = @UUID_LIBS_STATIC@ @BLKID_LIBS_STATIC@ -L. -pthread
STATIC_LIBS_COMP = @ZLIB_LIBS_STATIC@ @LZO2_LIBS_STATIC@ @ZSTD_LIBS_STATIC@
@@ -27,7 +31,7 @@ prefix ?= @prefix@
exec_prefix = @exec_prefix@
bindir = @bindir@
libdir ?= @libdir@
-incdir = @includedir@/btrfs
+incdir = @includedir@
udevdir = @UDEVDIR@
udevruledir = ${udevdir}/rules.d
diff --git a/VERSION b/VERSION
index 1597f02b..91137566 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-v4.15.1
+v4.16.1
diff --git a/btrfs-calc-size.c b/btrfs-calc-size.c
deleted file mode 100644
index d2d68ab2..00000000
--- a/btrfs-calc-size.c
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2011 Red Hat. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public
- * License v2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public
- * License along with this program; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 021110-1307, USA.
- */
-
-#include "volumes.h"
-#include "utils.h"
-#include "commands.h"
-#include "help.h"
-
-int main(int argc, char **argv)
-{
- int ret;
-
- warning(
-"\nthe tool has been deprecated, please use 'btrfs inspect-internal tree-stats' instead\n");
-
- if (argc > 1 && !strcmp(argv[1], "--help"))
- usage(cmd_inspect_tree_stats_usage);
-
- ret = cmd_inspect_tree_stats(argc, argv);
-
- btrfs_close_all_devices();
-
- return ret;
-}
diff --git a/btrfs-corrupt-block.c b/btrfs-corrupt-block.c
index 59ee1b45..da0ec8c5 100644
--- a/btrfs-corrupt-block.c
+++ b/btrfs-corrupt-block.c
@@ -726,7 +726,7 @@ out:
static void shift_items(struct btrfs_root *root, struct extent_buffer *eb)
{
int nritems = btrfs_header_nritems(eb);
- int shift_space = btrfs_leaf_free_space(root, eb) / 2;
+ int shift_space = btrfs_leaf_free_space(root->fs_info, eb) / 2;
int slot = nritems / 2;
int i = 0;
unsigned int data_end = btrfs_item_offset_nr(eb, nritems - 1);
diff --git a/btrfs-debug-tree.c b/btrfs-debug-tree.c
deleted file mode 100644
index 7bee018f..00000000
--- a/btrfs-debug-tree.c
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2007 Oracle. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public
- * License v2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public
- * License along with this program; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 021110-1307, USA.
- */
-
-#include "disk-io.h"
-#include "volumes.h"
-#include "utils.h"
-#include "commands.h"
-#include "help.h"
-
-int main(int argc, char **argv)
-{
- int ret;
-
- set_argv0(argv);
-
- if (argc > 1 && !strcmp(argv[1], "--help"))
- usage(cmd_inspect_dump_tree_usage);
-
- radix_tree_init();
-
- ret = cmd_inspect_dump_tree(argc, argv);
-
- btrfs_close_all_devices();
-
- return ret;
-}
diff --git a/btrfs-sb-mod.c b/btrfs-sb-mod.c
new file mode 100644
index 00000000..c381fb60
--- /dev/null
+++ b/btrfs-sb-mod.c
@@ -0,0 +1,337 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#include "kerncompat.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#include <inttypes.h>
+#include <string.h>
+#include <limits.h>
+#include <byteswap.h>
+#include <kernel-lib/crc32c.h>
+#include "disk-io.h"
+
+#define BLOCKSIZE (4096)
+static char buf[BLOCKSIZE];
+static int csum_size;
+
+static int check_csum_superblock(void *sb)
+{
+ u8 result[csum_size];
+ u32 crc = ~(u32)0;
+
+ crc = btrfs_csum_data((char *)sb + BTRFS_CSUM_SIZE,
+ crc, BTRFS_SUPER_INFO_SIZE - BTRFS_CSUM_SIZE);
+ btrfs_csum_final(crc, result);
+
+ return !memcmp(sb, &result, csum_size);
+}
+
+static void update_block_csum(void *block, int is_sb)
+{
+ u8 result[csum_size];
+ struct btrfs_header *hdr;
+ u32 crc = ~(u32)0;
+
+ if (is_sb) {
+ crc = btrfs_csum_data((char *)block + BTRFS_CSUM_SIZE, crc,
+ BTRFS_SUPER_INFO_SIZE - BTRFS_CSUM_SIZE);
+ } else {
+ crc = btrfs_csum_data((char *)block + BTRFS_CSUM_SIZE, crc,
+ BLOCKSIZE - BTRFS_CSUM_SIZE);
+ }
+ btrfs_csum_final(crc, result);
+ memset(block, 0, BTRFS_CSUM_SIZE);
+ hdr = (struct btrfs_header *)block;
+ memcpy(&hdr->csum, result, csum_size);
+}
+
+static u64 arg_strtou64(const char *str)
+{
+ u64 value;
+ char *ptr_parse_end = NULL;
+
+ value = strtoull(str, &ptr_parse_end, 0);
+ if (ptr_parse_end && *ptr_parse_end != '\0') {
+ fprintf(stderr, "ERROR: %s is not a valid numeric value.\n",
+ str);
+ exit(1);
+ }
+
+ /*
+ * if we pass a negative number to strtoull, it will return an
+ * unexpected number to us, so let's do the check ourselves.
+ */
+ if (str[0] == '-') {
+ fprintf(stderr, "ERROR: %s: negative value is invalid.\n",
+ str);
+ exit(1);
+ }
+ if (value == ULLONG_MAX) {
+ fprintf(stderr, "ERROR: %s is too large.\n", str);
+ exit(1);
+ }
+ return value;
+}
+
+
+enum field_op {
+ OP_GET,
+ OP_SET,
+ OP_ADD,
+ OP_SUB,
+ OP_XOR,
+ OP_NAND, /* broken */
+ OP_BSWAP,
+};
+
+struct fspec {
+ const char *name;
+ enum field_op fop;
+ u64 value;
+};
+
+enum field_type {
+ TYPE_UNKNOWN,
+ TYPE_U64,
+ TYPE_U16,
+};
+
+struct sb_field {
+ const char *name;
+ enum field_type type;
+} known_fields[] = {
+ { .name = "total_bytes", .type = TYPE_U64 },
+ { .name = "root", .type = TYPE_U64 },
+ { .name = "generation", .type = TYPE_U64 },
+ { .name = "chunk_root", .type = TYPE_U64 },
+ { .name = "chunk_root_generation", .type = TYPE_U64 },
+ { .name = "cache_generation", .type = TYPE_U64 },
+ { .name = "uuid_tree_generation", .type = TYPE_U64 },
+ { .name = "compat_flags", .type = TYPE_U64 },
+ { .name = "compat_ro_flags", .type = TYPE_U64 },
+ { .name = "incompat_flags", .type = TYPE_U64 },
+ { .name = "csum_type", .type = TYPE_U16 },
+};
+
+#define MOD_FIELD_XX(fname, set, val, bits, f_dec, f_hex, f_type) \
+ else if (strcmp(name, #fname) == 0) { \
+ if (set) { \
+ printf("SET: "#fname" "f_dec" (0x"f_hex")\n", \
+ (f_type)*val, (f_type)*val); \
+ sb->fname = cpu_to_le##bits(*val); \
+ } else { \
+ *val = le##bits##_to_cpu(sb->fname); \
+ printf("GET: "#fname" "f_dec" (0x"f_hex")\n", \
+ (f_type)*val, (f_type)*val); \
+ } \
+ }
+
+#define MOD_FIELD64(fname, set, val) \
+ MOD_FIELD_XX(fname, set, val, 64, "%llu", "%llx", unsigned long long)
+
+/* Alias for u64 */
+#define MOD_FIELD(fname, set, val) MOD_FIELD64(fname, set, val)
+
+/*
+ * Support only GET and SET properly, ADD and SUB may work
+ */
+#define MOD_FIELD32(fname, set, val) \
+ MOD_FIELD_XX(fname, set, val, 32, "%u", "%x", unsigned int)
+
+#define MOD_FIELD16(fname, set, val) \
+ MOD_FIELD_XX(fname, set, val, 16, "%hu", "%hx", unsigned short int)
+
+#define MOD_FIELD8(fname, set, val) \
+ MOD_FIELD_XX(fname, set, val, 8, "%hhu", "%hhx", unsigned char)
+
+static void mod_field_by_name(struct btrfs_super_block *sb, int set, const char *name,
+ u64 *val)
+{
+ if (0) { }
+ MOD_FIELD(total_bytes, set, val)
+ MOD_FIELD(root, set, val)
+ MOD_FIELD(generation, set, val)
+ MOD_FIELD(chunk_root, set, val)
+ MOD_FIELD(chunk_root_generation, set, val)
+ MOD_FIELD(cache_generation, set, val)
+ MOD_FIELD(uuid_tree_generation, set, val)
+ MOD_FIELD(compat_flags, set, val)
+ MOD_FIELD(compat_ro_flags, set, val)
+ MOD_FIELD(incompat_flags, set, val)
+ MOD_FIELD16(csum_type, set, val)
+ else {
+ printf("ERROR: unhandled field: %s\n", name);
+ exit(1);
+ }
+}
+
+static int sb_edit(struct btrfs_super_block *sb, struct fspec *fsp)
+{
+ u64 val;
+ u64 newval;
+
+ mod_field_by_name(sb, 0, fsp->name, &val);
+ switch (fsp->fop) {
+ case OP_GET: newval = val; break;
+ case OP_SET: newval = fsp->value; break;
+ case OP_ADD: newval = val + fsp->value; break;
+ case OP_SUB: newval = val - fsp->value; break;
+ case OP_XOR: newval = val ^ fsp->value; break;
+ case OP_NAND: newval = val & (~fsp->value); break;
+ case OP_BSWAP: newval = bswap_64(val); break;
+ default: printf("ERROR: unhandled operation: %d\n", fsp->fop); exit(1);
+ }
+
+ mod_field_by_name(sb, 1, fsp->name, &newval);
+
+ return 0;
+}
+
+static int is_known_field(const char *f)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(known_fields); i++)
+ if (strcmp(f, known_fields[i].name) == 0)
+ return 1;
+ return 0;
+}
+
+static int arg_to_op_value(const char *arg, enum field_op *op, u64 *val)
+{
+ switch (arg[0]) {
+ case 0: return -1;
+ case '.':
+ case '?': *op = OP_GET; *val = 0; break;
+ case '=': *op = OP_SET; *val = arg_strtou64(arg + 1); break;
+ case '+': *op = OP_ADD; *val = arg_strtou64(arg + 1); break;
+ case '-': *op = OP_SUB; *val = arg_strtou64(arg + 1); break;
+ case '^': *op = OP_XOR; *val = arg_strtou64(arg + 1); break;
+ case '~': *op = OP_NAND; *val = arg_strtou64(arg + 1); break;
+ case '@': *op = OP_BSWAP; *val = arg_strtou64(arg + 1); break;
+ default:
+ printf("ERROR: unknown op: %c\n", arg[0]);
+ return -1;
+ }
+
+ return 0;
+}
+
+int main(int argc, char **argv) {
+ int fd;
+ loff_t off;
+ int ret;
+ struct btrfs_header *hdr;
+ struct btrfs_super_block *sb;
+ int i;
+ struct fspec spec[128];
+ int specidx;
+ int changed;
+
+ memset(spec, 0, sizeof(spec));
+ if (argc <= 2) {
+ printf("Usage: %s image [fieldspec...]\n", argv[0]);
+ exit(1);
+ }
+ fd = open(argv[1], O_RDWR | O_EXCL);
+ if (fd == -1) {
+ perror("open()");
+ exit(1);
+ }
+
+ /* verify superblock */
+ csum_size = btrfs_csum_sizes[BTRFS_CSUM_TYPE_CRC32];
+ off = BTRFS_SUPER_INFO_OFFSET;
+
+ ret = pread(fd, buf, BLOCKSIZE, off);
+ if (ret <= 0) {
+ printf("pread error %d at offset %llu\n",
+ ret, (unsigned long long)off);
+ exit(1);
+ }
+ if (ret != BLOCKSIZE) {
+ printf("pread error at offset %llu: read only %d bytes\n",
+ (unsigned long long)off, ret);
+ exit(1);
+ }
+ hdr = (struct btrfs_header *)buf;
+ /* verify checksum */
+ if (!check_csum_superblock(&hdr->csum)) {
+ printf("super block checksum does not match at offset %llu, will be corrected\n",
+ (unsigned long long)off);
+ } else {
+ printf("super block checksum is ok\n");
+ }
+ sb = (struct btrfs_super_block *)buf;
+ changed = 0;
+
+ specidx = 0;
+ for (i = 2; i < argc; i++) {
+ struct fspec *f;
+
+ if (i + 1 >= argc) {
+ printf("ERROR: bad argument count\n");
+ ret = 1;
+ goto out;
+ }
+
+ if (!is_known_field(argv[i])) {
+ printf("ERROR: unknown filed: %s\n", argv[i]);
+ ret = 1;
+ goto out;
+ }
+ f = &spec[specidx];
+ specidx++;
+ f->name = strdup(argv[i]);
+ i++;
+ if (arg_to_op_value(argv[i], &f->fop, &f->value)) {
+ ret = 1;
+ goto out;
+ }
+ }
+
+ for (i = 0; i < specidx; i++) {
+ sb_edit(sb, &spec[i]);
+ changed = 1;
+ }
+
+ if (changed) {
+ printf("Update csum\n");
+ update_block_csum(buf, 1);
+ ret = pwrite(fd, buf, BLOCKSIZE, off);
+ if (ret <= 0) {
+ printf("pwrite error %d at offset %llu\n",
+ ret, (unsigned long long)off);
+ exit(1);
+ }
+ if (ret != BLOCKSIZE) {
+ printf("pwrite error at offset %llu: written only %d bytes\n",
+ (unsigned long long)off, ret);
+ exit(1);
+ }
+ fsync(fd);
+ } else {
+ printf("Nothing changed\n");
+ }
+ ret = 0;
+out:
+ close(fd);
+ return ret;
+}
diff --git a/btrfs-show-super.c b/btrfs-show-super.c
deleted file mode 100644
index 4273e42d..00000000
--- a/btrfs-show-super.c
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2012 STRATO AG. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public
- * License v2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public
- * License along with this program; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 021110-1307, USA.
- */
-
-#include "utils.h"
-#include "commands.h"
-#include "help.h"
-
-int main(int argc, char **argv)
-{
-
- int ret;
-
- set_argv0(argv);
-
- warning(
-"\nthe tool has been deprecated, please use 'btrfs inspect-internal dump-super' instead\n");
-
- if (argc > 1 && !strcmp(argv[1], "--help"))
- usage(cmd_inspect_dump_super_usage);
-
- ret = cmd_inspect_dump_super(argc, argv);
-
- return ret;
-}
diff --git a/btrfs-zero-log.c b/btrfs-zero-log.c
deleted file mode 100644
index 2fce59e9..00000000
--- a/btrfs-zero-log.c
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2007 Oracle. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public
- * License v2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public
- * License along with this program; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 021110-1307, USA.
- */
-
-#include "kerncompat.h"
-
-#include <stdio.h>
-#include <unistd.h>
-#include "ctree.h"
-#include "disk-io.h"
-#include "transaction.h"
-#include "utils.h"
-#include "help.h"
-
-__attribute__((noreturn)) static void print_usage(void)
-{
- printf("usage: btrfs-zero-log dev\n");
- exit(1);
-}
-
-int main(int argc, char **argv)
-{
- struct btrfs_root *root;
- struct btrfs_trans_handle *trans;
- struct btrfs_super_block *sb;
- int ret;
-
- set_argv0(argv);
- if (check_argc_exact(argc - optind, 1))
- print_usage();
-
- radix_tree_init();
-
- printf("WARNING: this utility is deprecated, please use 'btrfs rescue zero-log'\n\n");
-
- if ((ret = check_mounted(argv[optind])) < 0) {
- fprintf(stderr, "ERROR: could not check mount status: %s\n", strerror(-ret));
- goto out;
- } else if (ret) {
- fprintf(stderr, "ERROR: %s is currently mounted\n", argv[optind]);
- ret = -EBUSY;
- goto out;
- }
-
- root = open_ctree(argv[optind], 0, OPEN_CTREE_WRITES | OPEN_CTREE_PARTIAL);
- if (!root) {
- fprintf(stderr, "ERROR: cannot open ctree\n");
- return 1;
- }
-
- sb = root->fs_info->super_copy;
- printf("Clearing log on %s, previous log_root %llu, level %u\n",
- argv[optind],
- (unsigned long long)btrfs_super_log_root(sb),
- (unsigned)btrfs_super_log_root_level(sb));
- trans = btrfs_start_transaction(root, 1);
- BUG_ON(IS_ERR(trans));
- btrfs_set_super_log_root(root->fs_info->super_copy, 0);
- btrfs_set_super_log_root_level(root->fs_info->super_copy, 0);
- btrfs_commit_transaction(trans, root);
- close_ctree(root);
-out:
- return !!ret;
-}
diff --git a/check/main.c b/check/main.c
index 97baae58..c4a1801f 100644
--- a/check/main.c
+++ b/check/main.c
@@ -560,6 +560,8 @@ static void print_inode_error(struct btrfs_root *root, struct inode_record *rec)
fprintf(stderr, ", bad file extent");
if (errors & I_ERR_FILE_EXTENT_OVERLAP)
fprintf(stderr, ", file extent overlap");
+ if (errors & I_ERR_FILE_EXTENT_TOO_LARGE)
+ fprintf(stderr, ", inline file extent too large");
if (errors & I_ERR_FILE_EXTENT_DISCOUNT)
fprintf(stderr, ", file extent discount");
if (errors & I_ERR_DIR_ISIZE_WRONG)
@@ -1433,6 +1435,8 @@ static int process_file_extent(struct btrfs_root *root,
u64 disk_bytenr = 0;
u64 extent_offset = 0;
u64 mask = root->fs_info->sectorsize - 1;
+ u32 max_inline_size = min_t(u32, mask,
+ BTRFS_MAX_INLINE_DATA_SIZE(root->fs_info));
int extent_type;
int ret;
@@ -1458,9 +1462,21 @@ static int process_file_extent(struct btrfs_root *root,
extent_type = btrfs_file_extent_type(eb, fi);
if (extent_type == BTRFS_FILE_EXTENT_INLINE) {
+ u8 compression = btrfs_file_extent_compression(eb, fi);
+ struct btrfs_item *item = btrfs_item_nr(slot);
+
num_bytes = btrfs_file_extent_inline_len(eb, slot, fi);
if (num_bytes == 0)
rec->errors |= I_ERR_BAD_FILE_EXTENT;
+ if (compression) {
+ if (btrfs_file_extent_inline_item_len(eb, item) >
+ max_inline_size ||
+ num_bytes > root->fs_info->sectorsize)
+ rec->errors |= I_ERR_FILE_EXTENT_TOO_LARGE;
+ } else {
+ if (num_bytes > max_inline_size)
+ rec->errors |= I_ERR_FILE_EXTENT_TOO_LARGE;
+ }
rec->found_size += num_bytes;
num_bytes = (num_bytes + mask) & ~mask;
} else if (extent_type == BTRFS_FILE_EXTENT_REG ||
@@ -1511,8 +1527,15 @@ static int process_file_extent(struct btrfs_root *root,
if (found < num_bytes)
rec->some_csum_missing = 1;
} else if (extent_type == BTRFS_FILE_EXTENT_PREALLOC) {
- if (found > 0)
- rec->errors |= I_ERR_ODD_CSUM_ITEM;
+ if (found > 0) {
+ ret = check_prealloc_extent_written(root->fs_info,
+ disk_bytenr,
+ num_bytes);
+ if (ret < 0)
+ return ret;
+ if (ret == 0)
+ rec->errors |= I_ERR_ODD_CSUM_ITEM;
+ }
}
}
return 0;
@@ -5339,7 +5362,9 @@ static int check_space_cache(struct btrfs_root *root)
error += ret;
} else {
ret = load_free_space_cache(root->fs_info, cache);
- if (!ret)
+ if (ret < 0)
+ error++;
+ if (ret <= 0)
continue;
}
@@ -5576,6 +5601,7 @@ static int check_csums(struct btrfs_root *root)
int ret;
u64 data_len;
unsigned long leaf_offset;
+ bool verify_csum = !!check_data_csum;
root = root->fs_info->csum_root;
if (!extent_buffer_uptodate(root->node)) {
@@ -5598,6 +5624,16 @@ static int check_csums(struct btrfs_root *root)
path.slots[0]--;
ret = 0;
+ /*
+ * For metadata dump (btrfs-image) all data is wiped so verifying data
+ * csum is meaningless and will always report csum error.
+ */
+ if (check_data_csum && (btrfs_super_flags(root->fs_info->super_copy) &
+ (BTRFS_SUPER_FLAG_METADUMP | BTRFS_SUPER_FLAG_METADUMP_V2))) {
+ printf("skip data csum verification for metadata dump\n");
+ verify_csum = false;
+ }
+
while (1) {
if (path.slots[0] >= btrfs_header_nritems(path.nodes[0])) {
ret = btrfs_next_leaf(root, &path);
@@ -5619,7 +5655,7 @@ static int check_csums(struct btrfs_root *root)
data_len = (btrfs_item_size_nr(leaf, path.slots[0]) /
csum_size) * root->fs_info->sectorsize;
- if (!check_data_csum)
+ if (!verify_csum)
goto skip_csum_check;
leaf_offset = btrfs_item_ptr_offset(leaf, path.slots[0]);
ret = check_extent_csums(root, key.offset, data_len,
@@ -5934,7 +5970,7 @@ static int run_next_block(struct btrfs_root *root,
goto out;
if (btrfs_is_leaf(buf)) {
- btree_space_waste += btrfs_leaf_free_space(root, buf);
+ btree_space_waste += btrfs_leaf_free_space(fs_info, buf);
for (i = 0; i < nritems; i++) {
struct btrfs_file_extent_item *fi;
@@ -6078,12 +6114,7 @@ static int run_next_block(struct btrfs_root *root,
}
} else {
int level;
- struct btrfs_key first_key;
- first_key.objectid = 0;
-
- if (nritems > 0)
- btrfs_item_key_to_cpu(buf, &first_key, 0);
level = btrfs_header_level(buf);
for (i = 0; i < nritems; i++) {
struct extent_record tmpl;
@@ -7638,28 +7669,6 @@ repair_abort:
return err;
}
-u64 calc_stripe_length(u64 type, u64 length, int num_stripes)
-{
- u64 stripe_size;
-
- if (type & BTRFS_BLOCK_GROUP_RAID0) {
- stripe_size = length;
- stripe_size /= num_stripes;
- } else if (type & BTRFS_BLOCK_GROUP_RAID10) {
- stripe_size = length * 2;
- stripe_size /= num_stripes;
- } else if (type & BTRFS_BLOCK_GROUP_RAID5) {
- stripe_size = length;
- stripe_size /= (num_stripes - 1);
- } else if (type & BTRFS_BLOCK_GROUP_RAID6) {
- stripe_size = length;
- stripe_size /= (num_stripes - 2);
- } else {
- stripe_size = length;
- }
- return stripe_size;
-}
-
/*
* Check the chunk with its block group/dev list ref:
* Return 0 if all refs seems valid.
diff --git a/check/mode-common.c b/check/mode-common.c
index 1b56a968..e857d44d 100644
--- a/check/mode-common.c
+++ b/check/mode-common.c
@@ -24,6 +24,262 @@
#include "check/mode-common.h"
/*
+ * Check if the inode referenced by the given data reference uses the extent
+ * at disk_bytenr as a non-prealloc extent.
+ *
+ * Returns 1 if true, 0 if false and < 0 on error.
+ */
+static int check_prealloc_data_ref(struct btrfs_fs_info *fs_info,
+ u64 disk_bytenr,
+ struct btrfs_extent_data_ref *dref,
+ struct extent_buffer *eb)
+{
+ u64 rootid = btrfs_extent_data_ref_root(eb, dref);
+ u64 objectid = btrfs_extent_data_ref_objectid(eb, dref);
+ u64 offset = btrfs_extent_data_ref_offset(eb, dref);
+ struct btrfs_root *root;
+ struct btrfs_key key;
+ struct btrfs_path path;
+ int ret;
+
+ btrfs_init_path(&path);
+ key.objectid = rootid;
+ key.type = BTRFS_ROOT_ITEM_KEY;
+ key.offset = (u64)-1;
+ root = btrfs_read_fs_root(fs_info, &key);
+ if (IS_ERR(root)) {
+ ret = PTR_ERR(root);
+ goto out;
+ }
+
+ key.objectid = objectid;
+ key.type = BTRFS_EXTENT_DATA_KEY;
+ key.offset = offset;
+ ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
+ if (ret > 0) {
+ fprintf(stderr,
+ "Missing file extent item for inode %llu, root %llu, offset %llu",
+ objectid, rootid, offset);
+ ret = -ENOENT;
+ }
+ if (ret < 0)
+ goto out;
+
+ while (true) {
+ struct btrfs_file_extent_item *fi;
+ int extent_type;
+
+ if (path.slots[0] >= btrfs_header_nritems(path.nodes[0])) {
+ ret = btrfs_next_leaf(root, &path);
+ if (ret < 0)
+ goto out;
+ if (ret > 0)
+ break;
+ }
+
+ btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
+ if (key.objectid != objectid ||
+ key.type != BTRFS_EXTENT_DATA_KEY)
+ break;
+
+ fi = btrfs_item_ptr(path.nodes[0], path.slots[0],
+ struct btrfs_file_extent_item);
+ extent_type = btrfs_file_extent_type(path.nodes[0], fi);
+ if (extent_type != BTRFS_FILE_EXTENT_REG &&
+ extent_type != BTRFS_FILE_EXTENT_PREALLOC)
+ goto next;
+
+ if (btrfs_file_extent_disk_bytenr(path.nodes[0], fi) !=
+ disk_bytenr)
+ break;
+
+ if (extent_type == BTRFS_FILE_EXTENT_REG) {
+ ret = 1;
+ goto out;
+ }
+next:
+ path.slots[0]++;
+ }
+ ret = 0;
+ out:
+ btrfs_release_path(&path);
+ return ret;
+}
+
+/*
+ * Check if a shared data reference points to a node that has a file extent item
+ * pointing to the extent at @disk_bytenr that is not of type prealloc.
+ *
+ * Returns 1 if true, 0 if false and < 0 on error.
+ */
+static int check_prealloc_shared_data_ref(struct btrfs_fs_info *fs_info,
+ u64 parent, u64 disk_bytenr)
+{
+ struct extent_buffer *eb;
+ u32 nr;
+ int i;
+ int ret = 0;
+
+ eb = read_tree_block(fs_info, parent, 0);
+ if (!extent_buffer_uptodate(eb)) {
+ ret = -EIO;
+ goto out;
+ }
+
+ nr = btrfs_header_nritems(eb);
+ for (i = 0; i < nr; i++) {
+ struct btrfs_key key;
+ struct btrfs_file_extent_item *fi;
+ int extent_type;
+
+ btrfs_item_key_to_cpu(eb, &key, i);
+ if (key.type != BTRFS_EXTENT_DATA_KEY)
+ continue;
+
+ fi = btrfs_item_ptr(eb, i, struct btrfs_file_extent_item);
+ extent_type = btrfs_file_extent_type(eb, fi);
+ if (extent_type != BTRFS_FILE_EXTENT_REG &&
+ extent_type != BTRFS_FILE_EXTENT_PREALLOC)
+ continue;
+
+ if (btrfs_file_extent_disk_bytenr(eb, fi) == disk_bytenr &&
+ extent_type == BTRFS_FILE_EXTENT_REG) {
+ ret = 1;
+ break;
+ }
+ }
+ out:
+ free_extent_buffer(eb);
+ return ret;
+}
+
+/*
+ * Check if a prealloc extent is shared by multiple inodes and if any inode has
+ * already written to that extent. This is to avoid emitting invalid warnings
+ * about odd csum items (a inode has an extent entirely marked as prealloc
+ * but another inode shares it and has already written to it).
+ *
+ * Note: right now it does not check if the number of checksum items in the
+ * csum tree matches the number of bytes written into the ex-prealloc extent.
+ * It's complex to deal with that because the prealloc extent might have been
+ * partially written through multiple inodes and we would have to keep track of
+ * ranges, merging them and notice ranges that fully or partially overlap, to
+ * avoid false reports of csum items missing for areas of the prealloc extent
+ * that were not written to - for example if we have a 1M prealloc extent, we
+ * can have only the first half of it written, but 2 different inodes refer to
+ * the its first half (through reflinks/cloning), so keeping a counter of bytes
+ * covered by checksum items is not enough, as the correct value would be 512K
+ * and not 1M (whence the need to track ranges).
+ *
+ * Returns 0 if the prealloc extent was not written yet by any inode, 1 if
+ * at least one other inode has written to it, and < 0 on error.
+ */
+int check_prealloc_extent_written(struct btrfs_fs_info *fs_info,
+ u64 disk_bytenr, u64 num_bytes)
+{
+ struct btrfs_path path;
+ struct btrfs_key key;
+ int ret;
+ struct btrfs_extent_item *ei;
+ u32 item_size;
+ unsigned long ptr;
+ unsigned long end;
+
+ key.objectid = disk_bytenr;
+ key.type = BTRFS_EXTENT_ITEM_KEY;
+ key.offset = num_bytes;
+
+ btrfs_init_path(&path);
+ ret = btrfs_search_slot(NULL, fs_info->extent_root, &key, &path, 0, 0);
+ if (ret > 0) {
+ fprintf(stderr,
+ "Missing extent item in extent tree for disk_bytenr %llu, num_bytes %llu\n",
+ disk_bytenr, num_bytes);
+ ret = -ENOENT;
+ }
+ if (ret < 0)
+ goto out;
+
+ /* First check all inline refs. */
+ ei = btrfs_item_ptr(path.nodes[0], path.slots[0],
+ struct btrfs_extent_item);
+ item_size = btrfs_item_size_nr(path.nodes[0], path.slots[0]);
+ ptr = (unsigned long)(ei + 1);
+ end = (unsigned long)ei + item_size;
+ while (ptr < end) {
+ struct btrfs_extent_inline_ref *iref;
+ int type;
+
+ iref = (struct btrfs_extent_inline_ref *)ptr;
+ type = btrfs_extent_inline_ref_type(path.nodes[0], iref);
+ ASSERT(type == BTRFS_EXTENT_DATA_REF_KEY ||
+ type == BTRFS_SHARED_DATA_REF_KEY);
+
+ if (type == BTRFS_EXTENT_DATA_REF_KEY) {
+ struct btrfs_extent_data_ref *dref;
+
+ dref = (struct btrfs_extent_data_ref *)(&iref->offset);
+ ret = check_prealloc_data_ref(fs_info, disk_bytenr,
+ dref, path.nodes[0]);
+ if (ret != 0)
+ goto out;
+ } else if (type == BTRFS_SHARED_DATA_REF_KEY) {
+ u64 parent;
+
+ parent = btrfs_extent_inline_ref_offset(path.nodes[0],
+ iref);
+ ret = check_prealloc_shared_data_ref(fs_info,
+ parent,
+ disk_bytenr);
+ if (ret != 0)
+ goto out;
+ }
+
+ ptr += btrfs_extent_inline_ref_size(type);
+ }
+
+ /* Now check if there are any non-inlined refs. */
+ path.slots[0]++;
+ while (true) {
+ if (path.slots[0] >= btrfs_header_nritems(path.nodes[0])) {
+ ret = btrfs_next_leaf(fs_info->extent_root, &path);
+ if (ret < 0)
+ goto out;
+ if (ret > 0) {
+ ret = 0;
+ break;
+ }
+ }
+
+ btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
+ if (key.objectid != disk_bytenr)
+ break;
+
+ if (key.type == BTRFS_EXTENT_DATA_REF_KEY) {
+ struct btrfs_extent_data_ref *dref;
+
+ dref = btrfs_item_ptr(path.nodes[0], path.slots[0],
+ struct btrfs_extent_data_ref);
+ ret = check_prealloc_data_ref(fs_info, disk_bytenr,
+ dref, path.nodes[0]);
+ if (ret != 0)
+ goto out;
+ } else if (key.type == BTRFS_SHARED_DATA_REF_KEY) {
+ ret = check_prealloc_shared_data_ref(fs_info,
+ key.offset,
+ disk_bytenr);
+ if (ret != 0)
+ goto out;
+ }
+
+ path.slots[0]++;
+ }
+out:
+ btrfs_release_path(&path);
+ return ret;
+}
+
+/*
* Search in csum tree to find how many bytes of range [@start, @start + @len)
* has the corresponding csum item.
*
diff --git a/check/mode-common.h b/check/mode-common.h
index ffae782b..877c1aa9 100644
--- a/check/mode-common.h
+++ b/check/mode-common.h
@@ -80,6 +80,8 @@ static inline int fs_root_objectid(u64 objectid)
return is_fstree(objectid);
}
+int check_prealloc_extent_written(struct btrfs_fs_info *fs_info,
+ u64 disk_bytenr, u64 num_bytes);
int count_csum_range(struct btrfs_fs_info *fs_info, u64 start,
u64 len, u64 *found);
int insert_inode_item(struct btrfs_trans_handle *trans,
diff --git a/check/mode-lowmem.c b/check/mode-lowmem.c
index 62bcf3d2..bfe45aba 100644
--- a/check/mode-lowmem.c
+++ b/check/mode-lowmem.c
@@ -390,7 +390,7 @@ static void account_bytes(struct btrfs_root *root, struct btrfs_path *path,
total_extent_tree_bytes += eb->len;
if (level == 0) {
- btree_space_waste += btrfs_leaf_free_space(root, eb);
+ btree_space_waste += btrfs_leaf_free_space(root->fs_info, eb);
} else {
free_nrs = (BTRFS_NODEPTRS_PER_BLOCK(root->fs_info) -
btrfs_header_nritems(eb));
@@ -1417,6 +1417,8 @@ static int check_file_extent(struct btrfs_root *root, struct btrfs_key *fkey,
u64 csum_found; /* In byte size, sectorsize aligned */
u64 search_start; /* Logical range start we search for csum */
u64 search_len; /* Logical range len we search for csum */
+ u32 max_inline_extent_size = min_t(u32, root->fs_info->sectorsize - 1,
+ BTRFS_MAX_INLINE_DATA_SIZE(root->fs_info));
unsigned int extent_type;
unsigned int is_hole;
int compressed = 0;
@@ -1440,6 +1442,32 @@ static int check_file_extent(struct btrfs_root *root, struct btrfs_key *fkey,
root->objectid, fkey->objectid, fkey->offset);
err |= FILE_EXTENT_ERROR;
}
+ if (compressed) {
+ if (extent_num_bytes > root->fs_info->sectorsize) {
+ error(
+"root %llu EXTENT_DATA[%llu %llu] too large inline extent ram size, have %llu, max: %u",
+ root->objectid, fkey->objectid,
+ fkey->offset, extent_num_bytes,
+ root->fs_info->sectorsize - 1);
+ err |= FILE_EXTENT_ERROR;
+ }
+ if (item_inline_len > max_inline_extent_size) {
+ error(
+"root %llu EXTENT_DATA[%llu %llu] too large inline extent on-disk size, have %u, max: %u",
+ root->objectid, fkey->objectid,
+ fkey->offset, item_inline_len,
+ max_inline_extent_size);
+ err |= FILE_EXTENT_ERROR;
+ }
+ } else {
+ if (extent_num_bytes > max_inline_extent_size) {
+ error(
+ "root %llu EXTENT_DATA[%llu %llu] too large inline extent size, have %llu, max: %u",
+ root->objectid, fkey->objectid, fkey->offset,
+ extent_num_bytes, max_inline_extent_size);
+ err |= FILE_EXTENT_ERROR;
+ }
+ }
if (!compressed && extent_num_bytes != item_inline_len) {
error(
"root %llu EXTENT_DATA[%llu %llu] wrong inline size, have: %llu, expected: %u",
@@ -1503,9 +1531,17 @@ static int check_file_extent(struct btrfs_root *root, struct btrfs_key *fkey,
csum_found, search_len);
} else if (extent_type == BTRFS_FILE_EXTENT_PREALLOC &&
csum_found > 0) {
- err |= ODD_CSUM_ITEM;
- error("root %llu EXTENT_DATA[%llu %llu] prealloc shouldn't have csum, but has: %llu",
- root->objectid, fkey->objectid, fkey->offset, csum_found);
+ ret = check_prealloc_extent_written(root->fs_info,
+ disk_bytenr, disk_num_bytes);
+ if (ret < 0)
+ return ret;
+ if (ret == 0) {
+ err |= ODD_CSUM_ITEM;
+ error(
+"root %llu EXTENT_DATA[%llu %llu] prealloc shouldn't have csum, but has: %llu",
+ root->objectid, fkey->objectid, fkey->offset,
+ csum_found);
+ }
}
/* Check EXTENT_DATA hole */
@@ -1861,6 +1897,24 @@ out:
return ret;
}
+static bool has_orphan_item(struct btrfs_root *root, u64 ino)
+{
+ struct btrfs_path path;
+ struct btrfs_key key;
+ int ret;
+
+ btrfs_init_path(&path);
+ key.objectid = BTRFS_ORPHAN_OBJECTID;
+ key.type = BTRFS_ORPHAN_ITEM_KEY;
+ key.offset = ino;
+
+ ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
+ btrfs_release_path(&path);
+ if (ret == 0)
+ return true;
+ return false;
+}
+
/*
* Check INODE_ITEM and related ITEMs (the same inode number)
* 1. check link count
@@ -1890,6 +1944,7 @@ static int check_inode_item(struct btrfs_root *root, struct btrfs_path *path,
u64 extent_size = 0;
unsigned int dir;
unsigned int nodatasum;
+ bool is_orphan = false;
int slot;
int ret;
int err = 0;
@@ -2040,10 +2095,11 @@ out:
root->objectid, inode_id, nlink, refs);
}
} else if (!nlink) {
- if (repair)
+ is_orphan = has_orphan_item(root, inode_id);
+ if (!is_orphan && repair)
ret = repair_inode_orphan_item_lowmem(root,
path, inode_id);
- if (!repair || ret) {
+ if (!is_orphan && (!repair || ret)) {
err |= ORPHAN_ITEM;
error("root %llu INODE[%llu] is orphan item",
root->objectid, inode_id);
@@ -2631,9 +2687,9 @@ static int check_extent_data_item(struct btrfs_root *root,
if (!(extent_flags & BTRFS_EXTENT_FLAG_DATA)) {
error(
- "extent[%llu %llu] backref type mismatch, wanted bit: %llx",
- disk_bytenr, disk_num_bytes,
- BTRFS_EXTENT_FLAG_DATA);
+"file extent[%llu %llu] root %llu owner %llu backref type mismatch, wanted bit: %llx",
+ fi_key.objectid, fi_key.offset, root->objectid, owner,
+ BTRFS_EXTENT_FLAG_DATA);
err |= BACKREF_MISMATCH;
}
@@ -2689,8 +2745,8 @@ static int check_extent_data_item(struct btrfs_root *root,
/* Didn't find inlined data backref, try EXTENT_DATA_REF_KEY */
dbref_key.objectid = btrfs_file_extent_disk_bytenr(eb, fi);
dbref_key.type = BTRFS_EXTENT_DATA_REF_KEY;
- dbref_key.offset = hash_extent_data_ref(root->objectid,
- fi_key.objectid, fi_key.offset - offset);
+ dbref_key.offset = hash_extent_data_ref(owner, fi_key.objectid,
+ fi_key.offset - offset);
ret = btrfs_search_slot(NULL, root->fs_info->extent_root,
&dbref_key, &path, 0, 0);
@@ -2722,8 +2778,9 @@ out:
err |= BACKREF_MISSING;
btrfs_release_path(&path);
if (err & BACKREF_MISSING) {
- error("data extent[%llu %llu] backref lost",
- disk_bytenr, disk_num_bytes);
+ error(
+ "file extent[%llu %llu] root %llu owner %llu backref lost",
+ fi_key.objectid, fi_key.offset, root->objectid, owner);
}
return err;
}
diff --git a/check/mode-original.h b/check/mode-original.h
index f859af47..368de692 100644
--- a/check/mode-original.h
+++ b/check/mode-original.h
@@ -185,6 +185,7 @@ struct file_extent_hole {
#define I_ERR_SOME_CSUM_MISSING (1 << 12)
#define I_ERR_LINK_COUNT_WRONG (1 << 13)
#define I_ERR_FILE_EXTENT_ORPHAN (1 << 14)
+#define I_ERR_FILE_EXTENT_TOO_LARGE (1 << 15)
struct inode_record {
struct list_head backrefs;
diff --git a/cmds-fi-du.c b/cmds-fi-du.c
index 8a44665c..7e6bb7f6 100644
--- a/cmds-fi-du.c
+++ b/cmds-fi-du.c
@@ -449,7 +449,7 @@ static int du_add_file(const char *filename, int dirfd,
}
pathtmp = pathp;
- if (pathp == path)
+ if (pathp == path || *(pathp - 1) == '/')
ret = sprintf(pathp, "%s", filename);
else
ret = sprintf(pathp, "/%s", filename);
diff --git a/cmds-filesystem.c b/cmds-filesystem.c
index 467aff11..30a50bf5 100644
--- a/cmds-filesystem.c
+++ b/cmds-filesystem.c
@@ -28,6 +28,8 @@
#include <linux/limits.h>
#include <getopt.h>
+#include <btrfsutil.h>
+
#include "kerncompat.h"
#include "ctree.h"
#include "utils.h"
@@ -813,25 +815,16 @@ static const char * const cmd_filesystem_sync_usage[] = {
static int cmd_filesystem_sync(int argc, char **argv)
{
- int fd, res;
- char *path;
- DIR *dirstream = NULL;
+ enum btrfs_util_error err;
clean_args_no_options(argc, argv, cmd_filesystem_sync_usage);
if (check_argc_exact(argc - optind, 1))
usage(cmd_filesystem_sync_usage);
- path = argv[optind];
-
- fd = btrfs_open_dir(path, &dirstream, 1);
- if (fd < 0)
- return 1;
-
- res = ioctl(fd, BTRFS_IOC_SYNC);
- close_file_or_dir(fd, dirstream);
- if( res < 0 ){
- error("sync ioctl failed on '%s': %m", path);
+ err = btrfs_util_sync(argv[optind]);
+ if (err) {
+ error_btrfs_util(err);
return 1;
}
@@ -1093,7 +1086,7 @@ static int cmd_filesystem_resize(int argc, char **argv)
DIR *dirstream = NULL;
struct stat st;
- clean_args_no_options_relaxed(argc, argv, cmd_filesystem_resize_usage);
+ clean_args_no_options_relaxed(argc, argv);
if (check_argc_exact(argc - optind, 2))
usage(cmd_filesystem_resize_usage);
diff --git a/cmds-inspect-dump-super.c b/cmds-inspect-dump-super.c
index 150c2e5a..e965267c 100644
--- a/cmds-inspect-dump-super.c
+++ b/cmds-inspect-dump-super.c
@@ -339,7 +339,10 @@ static void dump_superblock(struct btrfs_super_block *sb, int full)
printf("csum\t\t\t0x");
for (i = 0, p = sb->csum; i < csum_size; i++)
printf("%02x", p[i]);
- if (check_csum_sblock(sb, csum_size))
+ if (csum_type != BTRFS_CSUM_TYPE_CRC32 ||
+ csum_size != btrfs_csum_sizes[BTRFS_CSUM_TYPE_CRC32])
+ printf(" [UNKNOWN CSUM TYPE OR SIZE]");
+ else if (check_csum_sblock(sb, csum_size))
printf(" [match]");
else
printf(" [DON'T MATCH]");
diff --git a/cmds-inspect-dump-tree.c b/cmds-inspect-dump-tree.c
index df44bb63..92a2a45b 100644
--- a/cmds-inspect-dump-tree.c
+++ b/cmds-inspect-dump-tree.c
@@ -33,8 +33,9 @@
#include "utils.h"
#include "help.h"
-static void print_extents(struct btrfs_root *root, struct extent_buffer *eb)
+static void print_extents(struct extent_buffer *eb)
{
+ struct btrfs_fs_info *fs_info = eb->fs_info;
struct extent_buffer *next;
int i;
u32 nr;
@@ -43,13 +44,13 @@ static void print_extents(struct btrfs_root *root, struct extent_buffer *eb)
return;
if (btrfs_is_leaf(eb)) {
- btrfs_print_leaf(root, eb);
+ btrfs_print_leaf(eb);
return;
}
nr = btrfs_header_nritems(eb);
for (i = 0; i < nr; i++) {
- next = read_tree_block(root->fs_info,
+ next = read_tree_block(fs_info,
btrfs_node_blockptr(eb, i),
btrfs_node_ptr_generation(eb, i));
if (!extent_buffer_uptodate(next))
@@ -68,7 +69,7 @@ static void print_extents(struct btrfs_root *root, struct extent_buffer *eb)
btrfs_header_level(eb));
goto out;
}
- print_extents(root, next);
+ print_extents(next);
free_extent_buffer(next);
}
@@ -198,6 +199,7 @@ const char * const cmd_inspect_dump_tree_usage[] = {
"-u|--uuid print only the uuid tree",
"-b|--block <block_num> print info from the specified block only",
"-t|--tree <tree_id> print only tree with the given id (string or number)",
+ "--follow use with -b, to show all children tree blocks of <block_num>",
NULL
};
@@ -219,13 +221,23 @@ int cmd_inspect_dump_tree(int argc, char **argv)
int uuid_tree_only = 0;
int roots_only = 0;
int root_backups = 0;
- unsigned open_ctree_flags = OPEN_CTREE_FS_PARTIAL;
+ unsigned open_ctree_flags;
u64 block_only = 0;
struct btrfs_root *tree_root_scan;
u64 tree_id = 0;
+ bool follow = false;
+ /*
+ * For debug-tree, we care nothing about extent tree (it's just backref
+ * and usage accounting, only makes sense for RW operations).
+ * Use NO_BLOCK_GROUPS here could also speedup open_ctree() and allow us
+ * to inspect fs with corrupted extent tree blocks, and show as many good
+ * tree blocks as possible.
+ */
+ open_ctree_flags = OPEN_CTREE_PARTIAL | OPEN_CTREE_NO_BLOCK_GROUPS;
while (1) {
int c;
+ enum { GETOPT_VAL_FOLLOW = 256 };
static const struct option long_options[] = {
{ "extents", no_argument, NULL, 'e'},
{ "device", no_argument, NULL, 'd'},
@@ -234,6 +246,7 @@ int cmd_inspect_dump_tree(int argc, char **argv)
{ "uuid", no_argument, NULL, 'u'},
{ "block", required_argument, NULL, 'b'},
{ "tree", required_argument, NULL, 't'},
+ { "follow", no_argument, NULL, GETOPT_VAL_FOLLOW },
{ NULL, 0, NULL, 0 }
};
@@ -286,6 +299,9 @@ int cmd_inspect_dump_tree(int argc, char **argv)
}
break;
}
+ case GETOPT_VAL_FOLLOW:
+ follow = true;
+ break;
default:
usage(cmd_inspect_dump_tree_usage);
}
@@ -324,7 +340,7 @@ int cmd_inspect_dump_tree(int argc, char **argv)
(unsigned long long)block_only);
goto close_root;
}
- btrfs_print_tree(root, leaf, 0);
+ btrfs_print_tree(leaf, follow);
free_extent_buffer(leaf);
goto close_root;
}
@@ -351,20 +367,17 @@ int cmd_inspect_dump_tree(int argc, char **argv)
} else {
if (info->tree_root->node) {
printf("root tree\n");
- btrfs_print_tree(info->tree_root,
- info->tree_root->node, 1);
+ btrfs_print_tree(info->tree_root->node, 1);
}
if (info->chunk_root->node) {
printf("chunk tree\n");
- btrfs_print_tree(info->chunk_root,
- info->chunk_root->node, 1);
+ btrfs_print_tree(info->chunk_root->node, 1);
}
if (info->log_root_tree) {
printf("log root tree\n");
- btrfs_print_tree(info->log_root_tree,
- info->log_root_tree->node, 1);
+ btrfs_print_tree(info->log_root_tree->node, 1);
}
}
}
@@ -384,7 +397,7 @@ again:
goto close_root;
}
printf("root tree\n");
- btrfs_print_tree(info->tree_root, info->tree_root->node, 1);
+ btrfs_print_tree(info->tree_root->node, 1);
goto close_root;
}
@@ -394,7 +407,7 @@ again:
goto close_root;
}
printf("chunk tree\n");
- btrfs_print_tree(info->chunk_root, info->chunk_root->node, 1);
+ btrfs_print_tree(info->chunk_root->node, 1);
goto close_root;
}
@@ -404,8 +417,7 @@ again:
goto close_root;
}
printf("log root tree\n");
- btrfs_print_tree(info->log_root_tree, info->log_root_tree->node,
- 1);
+ btrfs_print_tree(info->log_root_tree->node, 1);
goto close_root;
}
@@ -541,7 +553,7 @@ again:
printf(" tree ");
btrfs_print_key(&disk_key);
printf("\n");
- print_extents(tree_root_scan, buf);
+ print_extents(buf);
} else if (!skip) {
printf(" tree ");
btrfs_print_key(&disk_key);
@@ -551,7 +563,7 @@ again:
btrfs_header_level(buf));
} else {
printf(" \n");
- btrfs_print_tree(tree_root_scan, buf, 1);
+ btrfs_print_tree(buf, 1);
}
}
free_extent_buffer(buf);
diff --git a/cmds-qgroup.c b/cmds-qgroup.c
index 48686436..93206900 100644
--- a/cmds-qgroup.c
+++ b/cmds-qgroup.c
@@ -20,6 +20,8 @@
#include <unistd.h>
#include <getopt.h>
+#include <btrfsutil.h>
+
#include "ctree.h"
#include "ioctl.h"
@@ -299,6 +301,7 @@ static int cmd_qgroup_show(int argc, char **argv)
int filter_flag = 0;
unsigned unit_mode;
int sync = 0;
+ enum btrfs_util_error err;
struct btrfs_qgroup_comparer_set *comparer_set;
struct btrfs_qgroup_filter_set *filter_set;
@@ -372,9 +375,10 @@ static int cmd_qgroup_show(int argc, char **argv)
}
if (sync) {
- ret = ioctl(fd, BTRFS_IOC_SYNC);
- if (ret < 0)
- warning("sync ioctl failed on '%s': %m", path);
+ err = btrfs_util_sync_fd(fd);
+ if (err)
+ warning("sync ioctl failed on '%s': %s", path,
+ strerror(errno));
}
if (filter_flag) {
@@ -423,6 +427,7 @@ static int cmd_qgroup_limit(int argc, char **argv)
int compressed = 0;
int exclusive = 0;
DIR *dirstream = NULL;
+ enum btrfs_util_error err;
while (1) {
int c = getopt(argc, argv, "ce");
@@ -463,13 +468,9 @@ static int cmd_qgroup_limit(int argc, char **argv)
if (argc - optind == 2) {
args.qgroupid = 0;
path = argv[optind + 1];
- ret = test_issubvolume(path);
- if (ret < 0) {
- error("cannot access '%s': %s", path, strerror(-ret));
- return 1;
- }
- if (!ret) {
- error("'%s' is not a subvolume", path);
+ err = btrfs_util_is_subvolume(path);
+ if (err) {
+ error_btrfs_util(err);
return 1;
}
/*
diff --git a/cmds-restore.c b/cmds-restore.c
index ade35f0f..f228acab 100644
--- a/cmds-restore.c
+++ b/cmds-restore.c
@@ -1281,8 +1281,15 @@ static struct btrfs_root *open_fs(const char *dev, u64 root_location,
for (i = super_mirror; i < BTRFS_SUPER_MIRROR_MAX; i++) {
bytenr = btrfs_sb_offset(i);
+
+ /*
+ * Restore won't allocate extent and doesn't care anything
+ * in extent tree. Skip block group item search will allow
+ * restore to be executed on heavily damaged fs.
+ */
fs_info = open_ctree_fs_info(dev, bytenr, root_location, 0,
- OPEN_CTREE_PARTIAL);
+ OPEN_CTREE_PARTIAL |
+ OPEN_CTREE_NO_BLOCK_GROUPS);
if (fs_info)
break;
fprintf(stderr, "Could not open root, trying backup super\n");
diff --git a/cmds-scrub.c b/cmds-scrub.c
index dabe7d9a..6b909f20 100644
--- a/cmds-scrub.c
+++ b/cmds-scrub.c
@@ -591,8 +591,6 @@ again:
ret = 0;
_SCRUB_KVREAD(ret, &i, data_extents_scrubbed, avail, l,
&p[curr]->p);
- _SCRUB_KVREAD(ret, &i, data_extents_scrubbed, avail, l,
- &p[curr]->p);
_SCRUB_KVREAD(ret, &i, tree_extents_scrubbed, avail, l,
&p[curr]->p);
_SCRUB_KVREAD(ret, &i, data_bytes_scrubbed, avail, l,
diff --git a/cmds-subvolume.c b/cmds-subvolume.c
index 8a473f7a..45363a5a 100644
--- a/cmds-subvolume.c
+++ b/cmds-subvolume.c
@@ -14,6 +14,7 @@
* Boston, MA 021110-1307, USA.
*/
+#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -28,6 +29,8 @@
#include <uuid/uuid.h>
#include <linux/magic.h>
+#include <btrfsutil.h>
+
#include "kerncompat.h"
#include "ioctl.h"
#include "qgroup.h"
@@ -39,38 +42,11 @@
#include "utils.h"
#include "help.h"
-static int is_subvolume_cleaned(int fd, u64 subvolid)
-{
- int ret;
- struct btrfs_ioctl_search_args args;
- struct btrfs_ioctl_search_key *sk = &args.key;
-
- sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
- sk->min_objectid = subvolid;
- sk->max_objectid = subvolid;
- sk->min_type = BTRFS_ROOT_ITEM_KEY;
- sk->max_type = BTRFS_ROOT_ITEM_KEY;
- sk->min_offset = 0;
- sk->max_offset = (u64)-1;
- sk->min_transid = 0;
- sk->max_transid = (u64)-1;
- sk->nr_items = 1;
-
- ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
- if (ret < 0)
- return -errno;
-
- if (sk->nr_items == 0)
- return 1;
-
- return 0;
-}
-
-static int wait_for_subvolume_cleaning(int fd, int count, u64 *ids,
- int sleep_interval)
+static int wait_for_subvolume_cleaning(int fd, size_t count, uint64_t *ids,
+ int sleep_interval)
{
- int ret;
- int i;
+ size_t i;
+ enum btrfs_util_error err;
while (1) {
int clean = 1;
@@ -78,16 +54,14 @@ static int wait_for_subvolume_cleaning(int fd, int count, u64 *ids,
for (i = 0; i < count; i++) {
if (!ids[i])
continue;
- ret = is_subvolume_cleaned(fd, ids[i]);
- if (ret < 0) {
- error(
- "cannot read status of dead subvolume %llu: %s",
- (unsigned long long)ids[i], strerror(-ret));
- return ret;
- }
- if (ret) {
- printf("Subvolume id %llu is gone\n", ids[i]);
+ err = btrfs_util_subvolume_info_fd(fd, ids[i], NULL);
+ if (err == BTRFS_UTIL_ERROR_SUBVOLUME_NOT_FOUND) {
+ printf("Subvolume id %" PRIu64 " is gone\n",
+ ids[i]);
ids[i] = 0;
+ } else if (err) {
+ error_btrfs_util(err);
+ return -errno;
} else {
clean = 0;
}
@@ -226,12 +200,18 @@ out:
static int wait_for_commit(int fd)
{
- int ret;
+ enum btrfs_util_error err;
+ uint64_t transid;
- ret = ioctl(fd, BTRFS_IOC_START_SYNC, NULL);
- if (ret < 0)
- return ret;
- return ioctl(fd, BTRFS_IOC_WAIT_SYNC, NULL);
+ err = btrfs_util_start_sync_fd(fd, &transid);
+ if (err)
+ return -1;
+
+ err = btrfs_util_wait_sync_fd(fd, transid);
+ if (err)
+ return -1;
+
+ return 0;
}
static const char * const cmd_subvol_delete_usage[] = {
@@ -255,7 +235,6 @@ static int cmd_subvol_delete(int argc, char **argv)
int res, ret = 0;
int cnt;
int fd = -1;
- struct btrfs_ioctl_vol_args args;
char *dname, *vname, *cpath;
char *dupdname = NULL;
char *dupvname = NULL;
@@ -267,6 +246,7 @@ static int cmd_subvol_delete(int argc, char **argv)
char uuidbuf[BTRFS_UUID_UNPARSED_SIZE];
struct seen_fsid *seen_fsid_hash[SEEN_FSID_HASH_SIZE] = { NULL, };
enum { COMMIT_AFTER = 1, COMMIT_EACH = 2 };
+ enum btrfs_util_error err;
while (1) {
int c;
@@ -310,14 +290,9 @@ static int cmd_subvol_delete(int argc, char **argv)
again:
path = argv[cnt];
- res = test_issubvolume(path);
- if (res < 0) {
- error("cannot access subvolume %s: %s", path, strerror(-res));
- ret = 1;
- goto out;
- }
- if (!res) {
- error("not a subvolume: %s", path);
+ err = btrfs_util_is_subvolume(path);
+ if (err) {
+ error_btrfs_util(err);
ret = 1;
goto out;
}
@@ -343,11 +318,10 @@ again:
printf("Delete subvolume (%s): '%s/%s'\n",
commit_mode == COMMIT_EACH || (commit_mode == COMMIT_AFTER && cnt + 1 == argc)
? "commit" : "no-commit", dname, vname);
- memset(&args, 0, sizeof(args));
- strncpy_null(args.name, vname);
- res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args);
- if(res < 0 ){
- error("cannot delete '%s/%s': %m", dname, vname);
+
+ err = btrfs_util_delete_subvolume_fd(fd, vname, 0);
+ if (err) {
+ error_btrfs_util(err);
ret = 1;
goto out;
}
@@ -364,7 +338,7 @@ again:
error("unable to get fsid for '%s': %s",
path, strerror(-res));
error(
- "delete suceeded but commit may not be done in the end");
+ "delete succeeded but commit may not be done in the end");
ret = 1;
goto out;
}
@@ -656,6 +630,7 @@ static int cmd_subvol_snapshot(int argc, char **argv)
char *dupdir = NULL;
char *newname;
char *dstdir;
+ enum btrfs_util_error err;
struct btrfs_ioctl_vol_args_v2 args;
struct btrfs_qgroup_inherit *inherit = NULL;
DIR *dirstream1 = NULL, *dirstream2 = NULL;
@@ -703,13 +678,9 @@ static int cmd_subvol_snapshot(int argc, char **argv)
dst = argv[optind + 1];
retval = 1; /* failure */
- res = test_issubvolume(subvol);
- if (res < 0) {
- error("cannot access subvolume %s: %s", subvol, strerror(-res));
- goto out;
- }
- if (!res) {
- error("not a subvolume: %s", subvol);
+ err = btrfs_util_is_subvolume(subvol);
+ if (err) {
+ error_btrfs_util(err);
goto out;
}
@@ -798,31 +769,25 @@ static const char * const cmd_subvol_get_default_usage[] = {
static int cmd_subvol_get_default(int argc, char **argv)
{
int fd = -1;
- int ret;
- char *subvol;
- struct btrfs_list_filter_set *filter_set;
- u64 default_id;
+ int ret = 1;
+ uint64_t default_id;
DIR *dirstream = NULL;
+ enum btrfs_util_error err;
+ struct btrfs_util_subvolume_info subvol;
+ char *path;
clean_args_no_options(argc, argv, cmd_subvol_get_default_usage);
if (check_argc_exact(argc - optind, 1))
usage(cmd_subvol_get_default_usage);
- subvol = argv[1];
- fd = btrfs_open_dir(subvol, &dirstream, 1);
+ fd = btrfs_open_dir(argv[1], &dirstream, 1);
if (fd < 0)
return 1;
- ret = btrfs_list_get_default_subvolume(fd, &default_id);
- if (ret) {
- error("failed to look up default subvolume: %m");
- goto out;
- }
-
- ret = 1;
- if (default_id == 0) {
- error("'default' dir item not found");
+ err = btrfs_util_get_default_subvolume_fd(fd, &default_id);
+ if (err) {
+ error_btrfs_util(err);
goto out;
}
@@ -833,24 +798,27 @@ static int cmd_subvol_get_default(int argc, char **argv)
goto out;
}
- filter_set = btrfs_list_alloc_filter_set();
- btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_ROOTID,
- default_id);
+ err = btrfs_util_subvolume_info_fd(fd, default_id, &subvol);
+ if (err) {
+ error_btrfs_util(err);
+ goto out;
+ }
- /* by default we shall print the following columns*/
- btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
- btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
- btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
- btrfs_list_setup_print_column(BTRFS_LIST_PATH);
+ err = btrfs_util_subvolume_path_fd(fd, default_id, &path);
+ if (err) {
+ error_btrfs_util(err);
+ goto out;
+ }
- ret = btrfs_list_subvols_print(fd, filter_set, NULL,
- BTRFS_LIST_LAYOUT_DEFAULT, 1, NULL);
+ printf("ID %" PRIu64 " gen %" PRIu64 " top level %" PRIu64 " path %s\n",
+ subvol.id, subvol.generation, subvol.parent_id, path);
- if (filter_set)
- free(filter_set);
+ free(path);
+
+ ret = 0;
out:
close_file_or_dir(fd, dirstream);
- return !!ret;
+ return ret;
}
static const char * const cmd_subvol_set_default_usage[] = {
@@ -864,11 +832,9 @@ static const char * const cmd_subvol_set_default_usage[] = {
static int cmd_subvol_set_default(int argc, char **argv)
{
- int ret=0, fd;
- u64 objectid;
- char *path;
- char *subvolid;
- DIR *dirstream = NULL;
+ u64 objectid;
+ char *path;
+ enum btrfs_util_error err;
clean_args_no_options(argc, argv, cmd_subvol_set_default_usage);
@@ -878,42 +844,17 @@ static int cmd_subvol_set_default(int argc, char **argv)
if (argc - optind == 1) {
/* path to the subvolume is specified */
+ objectid = 0;
path = argv[optind];
-
- ret = test_issubvolume(path);
- if (ret < 0) {
- error("stat error: %s", strerror(-ret));
- return 1;
- } else if (!ret) {
- error("'%s' is not a subvolume", path);
- return 1;
- }
-
- fd = btrfs_open_dir(path, &dirstream, 1);
- if (fd < 0)
- return 1;
-
- ret = lookup_path_rootid(fd, &objectid);
- if (ret) {
- error("unable to get subvol id: %s", strerror(-ret));
- close_file_or_dir(fd, dirstream);
- return 1;
- }
} else {
/* subvol id and path to the filesystem are specified */
- subvolid = argv[optind];
+ objectid = arg_strtou64(argv[optind]);
path = argv[optind + 1];
- objectid = arg_strtou64(subvolid);
-
- fd = btrfs_open_dir(path, &dirstream, 1);
- if (fd < 0)
- return 1;
}
- ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid);
- close_file_or_dir(fd, dirstream);
- if (ret < 0) {
- error("unable to set a new default subvolume: %m");
+ err = btrfs_util_set_default_subvolume(path, objectid);
+ if (err) {
+ error_btrfs_util(err);
return 1;
}
return 0;
@@ -932,6 +873,7 @@ static int cmd_subvol_find_new(int argc, char **argv)
char *subvol;
u64 last_gen;
DIR *dirstream = NULL;
+ enum btrfs_util_error err;
clean_args_no_options(argc, argv, cmd_subvol_find_new_usage);
@@ -941,13 +883,9 @@ static int cmd_subvol_find_new(int argc, char **argv)
subvol = argv[optind];
last_gen = arg_strtou64(argv[optind + 1]);
- ret = test_issubvolume(subvol);
- if (ret < 0) {
- error("cannot access subvolume %s: %s", subvol, strerror(-ret));
- return 1;
- }
- if (!ret) {
- error("not a subvolume: %s", subvol);
+ err = btrfs_util_is_subvolume(subvol);
+ if (err) {
+ error_btrfs_util(err);
return 1;
}
@@ -955,9 +893,9 @@ static int cmd_subvol_find_new(int argc, char **argv)
if (fd < 0)
return 1;
- ret = ioctl(fd, BTRFS_IOC_SYNC);
- if (ret < 0) {
- error("sync ioctl failed on '%s': %m", subvol);
+ err = btrfs_util_sync_fd(fd);
+ if (err) {
+ error_btrfs_util(err);
close_file_or_dir(fd, dirstream);
return 1;
}
@@ -980,19 +918,20 @@ static const char * const cmd_subvol_show_usage[] = {
static int cmd_subvol_show(int argc, char **argv)
{
- struct root_info get_ri;
- struct btrfs_list_filter_set *filter_set = NULL;
char tstr[256];
char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
char *fullpath = NULL;
- char raw_prefix[] = "\t\t\t\t";
int fd = -1;
int ret = 1;
DIR *dirstream1 = NULL;
int by_rootid = 0;
int by_uuid = 0;
- u64 rootid_arg;
+ u64 rootid_arg = 0;
u8 uuid_arg[BTRFS_UUID_SIZE];
+ struct btrfs_util_subvolume_iterator *iter;
+ struct btrfs_util_subvolume_info subvol;
+ char *subvol_path = NULL;
+ enum btrfs_util_error err;
while (1) {
int c;
@@ -1029,96 +968,142 @@ static int cmd_subvol_show(int argc, char **argv)
usage(cmd_subvol_show_usage);
}
- memset(&get_ri, 0, sizeof(get_ri));
fullpath = realpath(argv[optind], NULL);
if (!fullpath) {
error("cannot find real path for '%s': %m", argv[optind]);
goto out;
}
- if (by_rootid) {
- ret = get_subvol_info_by_rootid(fullpath, &get_ri, rootid_arg);
- } else if (by_uuid) {
- ret = get_subvol_info_by_uuid(fullpath, &get_ri, uuid_arg);
- } else {
- ret = get_subvol_info(fullpath, &get_ri);
+ fd = open_file_or_dir(fullpath, &dirstream1);
+ if (fd < 0) {
+ error("can't access '%s'", fullpath);
+ goto out;
}
- if (ret) {
- if (ret < 0) {
- error("Failed to get subvol info %s: %s",
- fullpath, strerror(-ret));
- } else {
- error("Failed to get subvol info %s: %d",
- fullpath, ret);
+ if (by_uuid) {
+ err = btrfs_util_create_subvolume_iterator_fd(fd,
+ BTRFS_FS_TREE_OBJECTID,
+ 0, &iter);
+ if (err) {
+ error_btrfs_util(err);
+ goto out;
+ }
+
+ for (;;) {
+ err = btrfs_util_subvolume_iterator_next_info(iter,
+ &subvol_path,
+ &subvol);
+ if (err == BTRFS_UTIL_ERROR_STOP_ITERATION) {
+ uuid_unparse(uuid_arg, uuidparse);
+ error("can't find uuid '%s' on '%s'", uuidparse,
+ fullpath);
+ btrfs_util_destroy_subvolume_iterator(iter);
+ goto out;
+ } else if (err) {
+ error_btrfs_util(err);
+ btrfs_util_destroy_subvolume_iterator(iter);
+ goto out;
+ }
+
+ if (uuid_compare(subvol.uuid, uuid_arg) == 0)
+ break;
+
+ free(subvol_path);
+ }
+ btrfs_util_destroy_subvolume_iterator(iter);
+ } else {
+ /*
+ * If !by_rootid, rootid_arg = 0, which means find the
+ * subvolume ID of the fd and use that.
+ */
+ err = btrfs_util_subvolume_info_fd(fd, rootid_arg, &subvol);
+ if (err) {
+ error_btrfs_util(err);
+ goto out;
+ }
+
+ err = btrfs_util_subvolume_path_fd(fd, subvol.id, &subvol_path);
+ if (err) {
+ error_btrfs_util(err);
+ goto out;
}
- return ret;
+
}
/* print the info */
- printf("%s\n", get_ri.full_path);
- printf("\tName: \t\t\t%s\n", get_ri.name);
+ printf("%s\n", subvol.id == BTRFS_FS_TREE_OBJECTID ? "/" : subvol_path);
+ printf("\tName: \t\t\t%s\n",
+ (subvol.id == BTRFS_FS_TREE_OBJECTID ? "<FS_TREE>" :
+ basename(subvol_path)));
- if (uuid_is_null(get_ri.uuid))
+ if (uuid_is_null(subvol.uuid))
strcpy(uuidparse, "-");
else
- uuid_unparse(get_ri.uuid, uuidparse);
+ uuid_unparse(subvol.uuid, uuidparse);
printf("\tUUID: \t\t\t%s\n", uuidparse);
- if (uuid_is_null(get_ri.puuid))
+ if (uuid_is_null(subvol.parent_uuid))
strcpy(uuidparse, "-");
else
- uuid_unparse(get_ri.puuid, uuidparse);
+ uuid_unparse(subvol.parent_uuid, uuidparse);
printf("\tParent UUID: \t\t%s\n", uuidparse);
- if (uuid_is_null(get_ri.ruuid))
+ if (uuid_is_null(subvol.received_uuid))
strcpy(uuidparse, "-");
else
- uuid_unparse(get_ri.ruuid, uuidparse);
+ uuid_unparse(subvol.received_uuid, uuidparse);
printf("\tReceived UUID: \t\t%s\n", uuidparse);
- if (get_ri.otime) {
+ if (subvol.otime.tv_sec) {
struct tm tm;
- localtime_r(&get_ri.otime, &tm);
+ localtime_r(&subvol.otime.tv_sec, &tm);
strftime(tstr, 256, "%Y-%m-%d %X %z", &tm);
} else
strcpy(tstr, "-");
printf("\tCreation time: \t\t%s\n", tstr);
- printf("\tSubvolume ID: \t\t%llu\n", get_ri.root_id);
- printf("\tGeneration: \t\t%llu\n", get_ri.gen);
- printf("\tGen at creation: \t%llu\n", get_ri.ogen);
- printf("\tParent ID: \t\t%llu\n", get_ri.ref_tree);
- printf("\tTop level ID: \t\t%llu\n", get_ri.top_id);
+ printf("\tSubvolume ID: \t\t%" PRIu64 "\n", subvol.id);
+ printf("\tGeneration: \t\t%" PRIu64 "\n", subvol.generation);
+ printf("\tGen at creation: \t%" PRIu64 "\n", subvol.otransid);
+ printf("\tParent ID: \t\t%" PRIu64 "\n", subvol.parent_id);
+ printf("\tTop level ID: \t\t%" PRIu64 "\n", subvol.parent_id);
- if (get_ri.flags & BTRFS_ROOT_SUBVOL_RDONLY)
+ if (subvol.flags & BTRFS_ROOT_SUBVOL_RDONLY)
printf("\tFlags: \t\t\treadonly\n");
else
printf("\tFlags: \t\t\t-\n");
/* print the snapshots of the given subvol if any*/
printf("\tSnapshot(s):\n");
- filter_set = btrfs_list_alloc_filter_set();
- btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT,
- (u64)(unsigned long)get_ri.uuid);
- btrfs_list_setup_print_column(BTRFS_LIST_PATH);
- fd = open_file_or_dir(fullpath, &dirstream1);
- if (fd < 0) {
- fprintf(stderr, "ERROR: can't access '%s'\n", fullpath);
- goto out;
+ err = btrfs_util_create_subvolume_iterator_fd(fd,
+ BTRFS_FS_TREE_OBJECTID, 0,
+ &iter);
+
+ for (;;) {
+ struct btrfs_util_subvolume_info subvol2;
+ char *path;
+
+ err = btrfs_util_subvolume_iterator_next_info(iter, &path, &subvol2);
+ if (err == BTRFS_UTIL_ERROR_STOP_ITERATION) {
+ break;
+ } else if (err) {
+ error_btrfs_util(err);
+ btrfs_util_destroy_subvolume_iterator(iter);
+ goto out;
+ }
+
+ if (uuid_compare(subvol2.parent_uuid, subvol.uuid) == 0)
+ printf("\t\t\t\t%s\n", path);
+
+ free(path);
}
- btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
- 1, raw_prefix);
+ btrfs_util_destroy_subvolume_iterator(iter);
+ ret = 0;
out:
- /* clean up */
- free(get_ri.path);
- free(get_ri.name);
- free(get_ri.full_path);
- free(filter_set);
-
+ free(subvol_path);
close_file_or_dir(fd, dirstream1);
free(fullpath);
return !!ret;
@@ -1137,160 +1122,15 @@ static const char * const cmd_subvol_sync_usage[] = {
NULL
};
-#if 0
-/*
- * If we're looking for any dead subvolume, take a shortcut and look
- * for any ORPHAN_ITEMs in the tree root
- */
-static int fs_has_dead_subvolumes(int fd)
-{
- int ret;
- struct btrfs_ioctl_search_args args;
- struct btrfs_ioctl_search_key *sk = &args.key;
- struct btrfs_ioctl_search_header sh;
- u64 min_subvolid = 0;
-
-again:
- sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
- sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
- sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
- sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
- sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
- sk->min_offset = min_subvolid;
- sk->max_offset = (u64)-1;
- sk->min_transid = 0;
- sk->max_transid = (u64)-1;
- sk->nr_items = 1;
-
- ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
- if (ret < 0)
- return -errno;
-
- if (!sk->nr_items)
- return 0;
-
- memcpy(&sh, args.buf, sizeof(sh));
- min_subvolid = sh.offset;
-
- /*
- * Verify that the root item is really there and we haven't hit
- * a stale orphan
- */
- sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
- sk->min_objectid = min_subvolid;
- sk->max_objectid = min_subvolid;
- sk->min_type = BTRFS_ROOT_ITEM_KEY;
- sk->max_type = BTRFS_ROOT_ITEM_KEY;
- sk->min_offset = 0;
- sk->max_offset = (u64)-1;
- sk->min_transid = 0;
- sk->max_transid = (u64)-1;
- sk->nr_items = 1;
-
- ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
- if (ret < 0)
- return -errno;
-
- /*
- * Stale orphan, try the next one
- */
- if (!sk->nr_items) {
- min_subvolid++;
- goto again;
- }
-
- return 1;
-}
-#endif
-
-#define SUBVOL_ID_BATCH 1024
-
-/*
- * Enumerate all dead subvolumes that exist in the filesystem.
- * Fill @ids and reallocate to bigger size if needed.
- */
-static int enumerate_dead_subvols(int fd, u64 **ids)
-{
- int ret;
- struct btrfs_ioctl_search_args args;
- struct btrfs_ioctl_search_key *sk = &args.key;
- int idx = 0;
- int count = 0;
-
- memset(&args, 0, sizeof(args));
-
- sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
- sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
- sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
- sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
- sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
- sk->min_offset = 0;
- sk->max_offset = (u64)-1;
- sk->min_transid = 0;
- sk->max_transid = (u64)-1;
- sk->nr_items = 4096;
-
- *ids = NULL;
- while (1) {
- struct btrfs_ioctl_search_header *sh;
- unsigned long off;
- int i;
-
- ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
- if (ret < 0)
- return -errno;
-
- if (!sk->nr_items)
- return idx;
-
- off = 0;
- for (i = 0; i < sk->nr_items; i++) {
- sh = (struct btrfs_ioctl_search_header*)(args.buf + off);
- off += sizeof(*sh);
-
- if (btrfs_search_header_type(sh)
- == BTRFS_ORPHAN_ITEM_KEY) {
- if (idx >= count) {
- u64 *newids;
-
- count += SUBVOL_ID_BATCH;
- newids = (u64*)realloc(*ids,
- count * sizeof(u64));
- if (!newids)
- return -ENOMEM;
- *ids = newids;
- }
- (*ids)[idx] = btrfs_search_header_offset(sh);
- idx++;
- }
- off += btrfs_search_header_len(sh);
-
- 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++;
- else
- break;
- if (sk->min_type != BTRFS_ORPHAN_ITEM_KEY)
- break;
- if (sk->min_objectid != BTRFS_ORPHAN_OBJECTID)
- break;
- }
-
- return idx;
-}
-
static int cmd_subvol_sync(int argc, char **argv)
{
int fd = -1;
- int i;
int ret = 1;
DIR *dirstream = NULL;
- u64 *ids = NULL;
- int id_count;
+ uint64_t *ids = NULL;
+ size_t id_count, i;
int sleep_interval = 1;
+ enum btrfs_util_error err;
while (1) {
int c = getopt(argc, argv, "s:");
@@ -1324,10 +1164,9 @@ static int cmd_subvol_sync(int argc, char **argv)
id_count = argc - optind;
if (!id_count) {
- id_count = enumerate_dead_subvols(fd, &ids);
- if (id_count < 0) {
- error("can't enumerate dead subvolumes: %s",
- strerror(-id_count));
+ err = btrfs_util_deleted_subvolumes_fd(fd, &ids, &id_count);
+ if (err) {
+ error_btrfs_util(err);
ret = 1;
goto out;
}
@@ -1336,7 +1175,7 @@ static int cmd_subvol_sync(int argc, char **argv)
goto out;
}
} else {
- ids = (u64*)malloc(id_count * sizeof(u64));
+ ids = malloc(id_count * sizeof(uint64_t));
if (!ids) {
error("not enough memory");
ret = 1;
@@ -1350,13 +1189,13 @@ static int cmd_subvol_sync(int argc, char **argv)
arg = argv[optind + i];
errno = 0;
id = strtoull(arg, NULL, 10);
- if (errno < 0) {
+ if (errno) {
error("unrecognized subvolume id %s", arg);
ret = 1;
goto out;
}
- if (id < BTRFS_FIRST_FREE_OBJECTID
- || id > BTRFS_LAST_FREE_OBJECTID) {
+ if (id < BTRFS_FIRST_FREE_OBJECTID ||
+ id > BTRFS_LAST_FREE_OBJECTID) {
error("subvolume id %s out of range", arg);
ret = 1;
goto out;
diff --git a/config.h.in b/config.h.in
index 42167c0a..0e40ae4b 100644
--- a/config.h.in
+++ b/config.h.in
@@ -30,6 +30,9 @@
/* We did not define FIEMAP_EXTENT_SHARED */
#undef HAVE_OWN_FIEMAP_EXTENT_SHARED_DEFINE
+/* Define to 1 if you have the `reallocarray' function. */
+#undef HAVE_REALLOCARRAY
+
/* Define to 1 if you have the <stdint.h> header file. */
#undef HAVE_STDINT_H
diff --git a/configure b/configure
index 67085908..bbfab671 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.15.1.
+# Generated by GNU Autoconf 2.69 for btrfs-progs v4.16.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.15.1'
-PACKAGE_STRING='btrfs-progs v4.15.1'
+PACKAGE_VERSION='v4.16.1'
+PACKAGE_STRING='btrfs-progs v4.16.1'
PACKAGE_BUGREPORT='linux-btrfs@vger.kernel.org'
PACKAGE_URL='http://btrfs.wiki.kernel.org'
@@ -632,6 +632,18 @@ LZO2_CFLAGS
LZO2_LIBS_STATIC
LZO2_LIBS
UDEVDIR
+PYTHON_BINDINGS
+PYTHON_LIBS
+PYTHON_CFLAGS
+pkgpyexecdir
+pyexecdir
+pkgpythondir
+pythondir
+PYTHON_PLATFORM
+PYTHON_EXEC_PREFIX
+PYTHON_PREFIX
+PYTHON_VERSION
+PYTHON
BTRFSRESTORE_ZSTD
ZSTD_LIBS_STATIC
ZSTD_LIBS
@@ -737,6 +749,7 @@ enable_documentation
enable_convert
with_convert
enable_zstd
+enable_python
'
ac_precious_vars='build_alias
host_alias
@@ -763,7 +776,10 @@ UUID_LIBS
ZLIB_CFLAGS
ZLIB_LIBS
ZSTD_CFLAGS
-ZSTD_LIBS'
+ZSTD_LIBS
+PYTHON
+PYTHON_CFLAGS
+PYTHON_LIBS'
# Initialize some variables set by options.
@@ -1304,7 +1320,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.15.1 to adapt to many kinds of systems.
+\`configure' configures btrfs-progs v4.16.1 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1369,7 +1385,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of btrfs-progs v4.15.1:";;
+ short | recursive ) echo "Configuration of btrfs-progs v4.16.1:";;
esac
cat <<\_ACEOF
@@ -1382,6 +1398,7 @@ Optional Features:
--disable-documentation do not build domumentation
--disable-convert do not build btrfs-convert
--disable-zstd build without zstd support
+ --disable-python do not build libbtrfsutil Python bindings
Optional Packages:
--with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
@@ -1424,6 +1441,10 @@ Some influential environment variables:
ZLIB_LIBS linker flags for ZLIB, overriding pkg-config
ZSTD_CFLAGS C compiler flags for ZSTD, overriding pkg-config
ZSTD_LIBS linker flags for ZSTD, overriding pkg-config
+ PYTHON the Python interpreter
+ PYTHON_CFLAGS
+ C compiler flags for PYTHON, overriding pkg-config
+ PYTHON_LIBS linker flags for PYTHON, overriding pkg-config
Use these variables to override the choices made by `configure' or to help
it to find libraries and programs with nonstandard names/locations.
@@ -1492,7 +1513,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
-btrfs-progs configure v4.15.1
+btrfs-progs configure v4.16.1
generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc.
@@ -1861,7 +1882,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.15.1, which was
+It was created by btrfs-progs $as_me v4.16.1, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
@@ -4951,6 +4972,18 @@ fi
done
+for ac_func in reallocarray
+do :
+ ac_fn_c_check_func "$LINENO" "reallocarray" "ac_cv_func_reallocarray"
+if test "x$ac_cv_func_reallocarray" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_REALLOCARRAY 1
+_ACEOF
+
+fi
+done
+
+
@@ -5168,7 +5201,6 @@ done
done
IFS=$as_save_IFS
- test -z "$ac_cv_path_XMLTO" && ac_cv_path_XMLTO="xmlto"
;;
esac
fi
@@ -5182,6 +5214,10 @@ $as_echo "no" >&6; }
fi
+ if test -z "$XMLTO"; then
+ as_fn_error $? "cannot find xmlto, cannot build documentation" "$LINENO" 5
+ fi
+
# Extract the first word of "gzip", so it can be a program name with args.
set dummy gzip; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
@@ -6142,12 +6178,12 @@ $as_echo "yes" >&6; }
fi
- if { { $as_echo "$as_me:${as_lineno-$LINENO}: pkg-config --exists --print-errors \"blkid\""; } >&5
- (pkg-config --exists --print-errors "blkid") 2>&5
+ if { { $as_echo "$as_me:${as_lineno-$LINENO}: \${PKG_CONFIG} --exists --print-errors \"blkid\""; } >&5
+ (${PKG_CONFIG} --exists --print-errors "blkid") 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
- BLKID_LIBS_STATIC=`pkg-config --libs --static "blkid"`
+ BLKID_LIBS_STATIC=`${PKG_CONFIG} --libs --static "blkid"`
else
as_fn_error $? "pkg-config description of blkid, needed for static build, is not available" "$LINENO" 5
@@ -6246,12 +6282,12 @@ $as_echo "yes" >&6; }
fi
- if { { $as_echo "$as_me:${as_lineno-$LINENO}: pkg-config --exists --print-errors \"uuid\""; } >&5
- (pkg-config --exists --print-errors "uuid") 2>&5
+ if { { $as_echo "$as_me:${as_lineno-$LINENO}: \${PKG_CONFIG} --exists --print-errors \"uuid\""; } >&5
+ (${PKG_CONFIG} --exists --print-errors "uuid") 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
- UUID_LIBS_STATIC=`pkg-config --libs --static "uuid"`
+ UUID_LIBS_STATIC=`${PKG_CONFIG} --libs --static "uuid"`
else
as_fn_error $? "pkg-config description of uuid, needed for static build, is not available" "$LINENO" 5
@@ -6350,12 +6386,12 @@ $as_echo "yes" >&6; }
fi
- if { { $as_echo "$as_me:${as_lineno-$LINENO}: pkg-config --exists --print-errors \"zlib\""; } >&5
- (pkg-config --exists --print-errors "zlib") 2>&5
+ if { { $as_echo "$as_me:${as_lineno-$LINENO}: \${PKG_CONFIG} --exists --print-errors \"zlib\""; } >&5
+ (${PKG_CONFIG} --exists --print-errors "zlib") 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
- ZLIB_LIBS_STATIC=`pkg-config --libs --static "zlib"`
+ ZLIB_LIBS_STATIC=`${PKG_CONFIG} --libs --static "zlib"`
else
as_fn_error $? "pkg-config description of zlib, needed for static build, is not available" "$LINENO" 5
@@ -6464,12 +6500,12 @@ $as_echo "yes" >&6; }
fi
- if { { $as_echo "$as_me:${as_lineno-$LINENO}: pkg-config --exists --print-errors \"libzstd\""; } >&5
- (pkg-config --exists --print-errors "libzstd") 2>&5
+ if { { $as_echo "$as_me:${as_lineno-$LINENO}: \${PKG_CONFIG} --exists --print-errors \"libzstd\""; } >&5
+ (${PKG_CONFIG} --exists --print-errors "libzstd") 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
- ZSTD_LIBS_STATIC=`pkg-config --libs --static "libzstd"`
+ ZSTD_LIBS_STATIC=`${PKG_CONFIG} --libs --static "libzstd"`
else
as_fn_error $? "pkg-config description of libzstd, needed for static build, is not available" "$LINENO" 5
@@ -6484,12 +6520,384 @@ else
fi
+# Check whether --enable-python was given.
+if test "${enable_python+set}" = set; then :
+ enableval=$enable_python;
+else
+ enable_python=yes
+
+fi
+
+
+if test "x$enable_python" = xyes; then
+
+
+
+
+
+
+ if test -n "$PYTHON"; then
+ # If the user set $PYTHON, use it and don't search something else.
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $PYTHON version is >= 3.4" >&5
+$as_echo_n "checking whether $PYTHON version is >= 3.4... " >&6; }
+ prog="import sys
+# split strings by '.' and convert to numeric. Append some zeros
+# because we need at least 4 digits for the hex conversion.
+# map returns an iterator in Python 3.0 and a list in 2.x
+minver = list(map(int, '3.4'.split('.'))) + [0, 0, 0]
+minverhex = 0
+# xrange is not present in Python 3.0 and range returns an iterator
+for i in list(range(0, 4)): minverhex = (minverhex << 8) + minver[i]
+sys.exit(sys.hexversion < minverhex)"
+ if { echo "$as_me:$LINENO: $PYTHON -c "$prog"" >&5
+ ($PYTHON -c "$prog") >&5 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ as_fn_error $? "Python interpreter is too old" "$LINENO" 5
+fi
+ am_display_PYTHON=$PYTHON
+ else
+ # Otherwise, try each interpreter until we find one that satisfies
+ # VERSION.
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a Python interpreter with version >= 3.4" >&5
+$as_echo_n "checking for a Python interpreter with version >= 3.4... " >&6; }
+if ${am_cv_pathless_PYTHON+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ for am_cv_pathless_PYTHON in python python2 python3 python3.5 python3.4 python3.3 python3.2 python3.1 python3.0 python2.7 python2.6 python2.5 python2.4 python2.3 python2.2 python2.1 python2.0 none; do
+ test "$am_cv_pathless_PYTHON" = none && break
+ prog="import sys
+# split strings by '.' and convert to numeric. Append some zeros
+# because we need at least 4 digits for the hex conversion.
+# map returns an iterator in Python 3.0 and a list in 2.x
+minver = list(map(int, '3.4'.split('.'))) + [0, 0, 0]
+minverhex = 0
+# xrange is not present in Python 3.0 and range returns an iterator
+for i in list(range(0, 4)): minverhex = (minverhex << 8) + minver[i]
+sys.exit(sys.hexversion < minverhex)"
+ if { echo "$as_me:$LINENO: $am_cv_pathless_PYTHON -c "$prog"" >&5
+ ($am_cv_pathless_PYTHON -c "$prog") >&5 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then :
+ break
+fi
+ done
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_pathless_PYTHON" >&5
+$as_echo "$am_cv_pathless_PYTHON" >&6; }
+ # Set $PYTHON to the absolute path of $am_cv_pathless_PYTHON.
+ if test "$am_cv_pathless_PYTHON" = none; then
+ PYTHON=:
+ else
+ # Extract the first word of "$am_cv_pathless_PYTHON", so it can be a program name with args.
+set dummy $am_cv_pathless_PYTHON; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_PYTHON+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $PYTHON in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_PYTHON="$PYTHON" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_path_PYTHON="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+PYTHON=$ac_cv_path_PYTHON
+if test -n "$PYTHON"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PYTHON" >&5
+$as_echo "$PYTHON" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ fi
+ am_display_PYTHON=$am_cv_pathless_PYTHON
+ fi
+
+
+ if test "$PYTHON" = :; then
+ as_fn_error $? "no suitable Python interpreter found" "$LINENO" 5
+ else
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $am_display_PYTHON version" >&5
+$as_echo_n "checking for $am_display_PYTHON version... " >&6; }
+if ${am_cv_python_version+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ am_cv_python_version=`$PYTHON -c "import sys; sys.stdout.write(sys.version[:3])"`
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_python_version" >&5
+$as_echo "$am_cv_python_version" >&6; }
+ PYTHON_VERSION=$am_cv_python_version
+
+
+
+ PYTHON_PREFIX='${prefix}'
+
+ PYTHON_EXEC_PREFIX='${exec_prefix}'
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $am_display_PYTHON platform" >&5
+$as_echo_n "checking for $am_display_PYTHON platform... " >&6; }
+if ${am_cv_python_platform+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ am_cv_python_platform=`$PYTHON -c "import sys; sys.stdout.write(sys.platform)"`
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_python_platform" >&5
+$as_echo "$am_cv_python_platform" >&6; }
+ PYTHON_PLATFORM=$am_cv_python_platform
+
+
+ # Just factor out some code duplication.
+ am_python_setup_sysconfig="\
+import sys
+# Prefer sysconfig over distutils.sysconfig, for better compatibility
+# with python 3.x. See automake bug#10227.
+try:
+ import sysconfig
+except ImportError:
+ can_use_sysconfig = 0
+else:
+ can_use_sysconfig = 1
+# Can't use sysconfig in CPython 2.7, since it's broken in virtualenvs:
+# <https://github.com/pypa/virtualenv/issues/118>
+try:
+ from platform import python_implementation
+ if python_implementation() == 'CPython' and sys.version[:3] == '2.7':
+ can_use_sysconfig = 0
+except ImportError:
+ pass"
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $am_display_PYTHON script directory" >&5
+$as_echo_n "checking for $am_display_PYTHON script directory... " >&6; }
+if ${am_cv_python_pythondir+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test "x$prefix" = xNONE
+ then
+ am_py_prefix=$ac_default_prefix
+ else
+ am_py_prefix=$prefix
+ fi
+ am_cv_python_pythondir=`$PYTHON -c "
+$am_python_setup_sysconfig
+if can_use_sysconfig:
+ sitedir = sysconfig.get_path('purelib', vars={'base':'$am_py_prefix'})
+else:
+ from distutils import sysconfig
+ sitedir = sysconfig.get_python_lib(0, 0, prefix='$am_py_prefix')
+sys.stdout.write(sitedir)"`
+ case $am_cv_python_pythondir in
+ $am_py_prefix*)
+ am__strip_prefix=`echo "$am_py_prefix" | sed 's|.|.|g'`
+ am_cv_python_pythondir=`echo "$am_cv_python_pythondir" | sed "s,^$am__strip_prefix,$PYTHON_PREFIX,"`
+ ;;
+ *)
+ case $am_py_prefix in
+ /usr|/System*) ;;
+ *)
+ am_cv_python_pythondir=$PYTHON_PREFIX/lib/python$PYTHON_VERSION/site-packages
+ ;;
+ esac
+ ;;
+ esac
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_python_pythondir" >&5
+$as_echo "$am_cv_python_pythondir" >&6; }
+ pythondir=$am_cv_python_pythondir
+
+
+
+ pkgpythondir=\${pythondir}/$PACKAGE
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $am_display_PYTHON extension module directory" >&5
+$as_echo_n "checking for $am_display_PYTHON extension module directory... " >&6; }
+if ${am_cv_python_pyexecdir+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test "x$exec_prefix" = xNONE
+ then
+ am_py_exec_prefix=$am_py_prefix
+ else
+ am_py_exec_prefix=$exec_prefix
+ fi
+ am_cv_python_pyexecdir=`$PYTHON -c "
+$am_python_setup_sysconfig
+if can_use_sysconfig:
+ sitedir = sysconfig.get_path('platlib', vars={'platbase':'$am_py_prefix'})
+else:
+ from distutils import sysconfig
+ sitedir = sysconfig.get_python_lib(1, 0, prefix='$am_py_prefix')
+sys.stdout.write(sitedir)"`
+ case $am_cv_python_pyexecdir in
+ $am_py_exec_prefix*)
+ am__strip_prefix=`echo "$am_py_exec_prefix" | sed 's|.|.|g'`
+ am_cv_python_pyexecdir=`echo "$am_cv_python_pyexecdir" | sed "s,^$am__strip_prefix,$PYTHON_EXEC_PREFIX,"`
+ ;;
+ *)
+ case $am_py_exec_prefix in
+ /usr|/System*) ;;
+ *)
+ am_cv_python_pyexecdir=$PYTHON_EXEC_PREFIX/lib/python$PYTHON_VERSION/site-packages
+ ;;
+ esac
+ ;;
+ esac
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_python_pyexecdir" >&5
+$as_echo "$am_cv_python_pyexecdir" >&6; }
+ pyexecdir=$am_cv_python_pyexecdir
+
+
+
+ pkgpyexecdir=\${pyexecdir}/$PACKAGE
+
+
+
+ fi
+
+
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for python-${PYTHON_VERSION}" >&5
+$as_echo_n "checking for python-${PYTHON_VERSION}... " >&6; }
+
+if test -n "$PYTHON_CFLAGS"; then
+ pkg_cv_PYTHON_CFLAGS="$PYTHON_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"python-\${PYTHON_VERSION}\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "python-${PYTHON_VERSION}") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_PYTHON_CFLAGS=`$PKG_CONFIG --cflags "python-${PYTHON_VERSION}" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+if test -n "$PYTHON_LIBS"; then
+ pkg_cv_PYTHON_LIBS="$PYTHON_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"python-\${PYTHON_VERSION}\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "python-${PYTHON_VERSION}") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_PYTHON_LIBS=`$PKG_CONFIG --libs "python-${PYTHON_VERSION}" 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
+ PYTHON_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "python-${PYTHON_VERSION}" 2>&1`
+ else
+ PYTHON_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "python-${PYTHON_VERSION}" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$PYTHON_PKG_ERRORS" >&5
+
+ as_fn_error $? "Package requirements (python-${PYTHON_VERSION}) were not met:
+
+$PYTHON_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 PYTHON_CFLAGS
+and PYTHON_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 PYTHON_CFLAGS
+and PYTHON_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
+ PYTHON_CFLAGS=$pkg_cv_PYTHON_CFLAGS
+ PYTHON_LIBS=$pkg_cv_PYTHON_LIBS
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+fi
+fi
+
+if test "x$enable_python" = xyes; then :
+ PYTHON_BINDINGS=1
+else
+ PYTHON_BINDINGS=0
+fi
+
+
+
# udev v190 introduced the btrfs builtin and a udev rule to use it.
# Our udev rule gives us the friendly dm names but isn't required (or valid)
# on earlier releases.
UDEVDIR=
-if pkg-config udev --atleast-version 190; then
- UDEVDIR="$(pkg-config udev --variable=udevdir)"
+if ${PKG_CONFIG} udev --atleast-version 190; then
+ UDEVDIR="$(${PKG_CONFIG} udev --variable=udevdir)"
fi
@@ -7062,7 +7470,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.15.1, which was
+This file was extended by btrfs-progs $as_me v4.16.1, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -7125,7 +7533,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.15.1
+btrfs-progs config.status v4.16.1
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
@@ -7857,6 +8265,8 @@ fi
backtrace support: ${enable_backtrace}
btrfs-convert: ${enable_convert} ${convertfs:+($convertfs)}
btrfs-restore zstd: ${enable_zstd}
+ Python bindings: ${enable_python}
+ Python interpreter: ${PYTHON}
Type 'make' to compile.
" >&5
@@ -7879,6 +8289,8 @@ $as_echo "
backtrace support: ${enable_backtrace}
btrfs-convert: ${enable_convert} ${convertfs:+($convertfs)}
btrfs-restore zstd: ${enable_zstd}
+ Python bindings: ${enable_python}
+ Python interpreter: ${PYTHON}
Type 'make' to compile.
" >&6; }
diff --git a/configure.ac b/configure.ac
index fcdde731..a4c98b31 100644
--- a/configure.ac
+++ b/configure.ac
@@ -43,6 +43,8 @@ AC_PATH_PROG([RMDIR], [rmdir], [rmdir])
AC_CHECK_FUNCS([openat], [],
[AC_MSG_ERROR([cannot find openat() function])])
+AC_CHECK_FUNCS([reallocarray])
+
m4_ifndef([PKG_PROG_PKG_CONFIG],
[m4_fatal([Could not locate the pkg-config autoconf
macros. These are usually located in /usr/share/aclocal/pkg.m4.
@@ -56,8 +58,8 @@ dnl
dnl Calls pkg-config --static
dnl
AC_DEFUN([PKG_STATIC], [
- if AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$2"]); then
- $1=`$PKG_CONFIG --libs --static "$2"`
+ if AC_RUN_LOG([${PKG_CONFIG} --exists --print-errors "$2"]); then
+ $1=`${PKG_CONFIG} --libs --static "$2"`
AC_SUBST([$1])
else
AC_MSG_ERROR([pkg-config description of $2, needed for static build, is not available])
@@ -91,7 +93,11 @@ AC_SUBST([DISABLE_DOCUMENTATION])
dnl detect tools to build documentation
ASCIIDOC_TOOL="none"
if test "x$enable_documentation" = xyes; then
- AC_PATH_PROG([XMLTO], [xmlto], [xmlto])
+ AC_PATH_PROG([XMLTO], [xmlto])
+ if test -z "$XMLTO"; then
+ AC_MSG_ERROR([cannot find xmlto, cannot build documentation])
+ fi
+
AC_PATH_PROG([GZIP], [gzip], [gzip])
AC_PATH_PROG([MV], [mv], [mv])
AC_PROG_SED
@@ -210,12 +216,26 @@ fi
AS_IF([test "x$enable_zstd" = xyes], [BTRFSRESTORE_ZSTD=1], [BTRFSRESTORE_ZSTD=0])
AC_SUBST(BTRFSRESTORE_ZSTD)
+AC_ARG_ENABLE([python],
+ AS_HELP_STRING([--disable-python], [do not build libbtrfsutil Python bindings]),
+ [], [enable_python=yes]
+)
+
+if test "x$enable_python" = xyes; then
+ AM_PATH_PYTHON([3.4])
+ PKG_CHECK_MODULES(PYTHON, [python-${PYTHON_VERSION}])
+fi
+
+AS_IF([test "x$enable_python" = xyes], [PYTHON_BINDINGS=1], [PYTHON_BINDINGS=0])
+AC_SUBST(PYTHON_BINDINGS)
+AC_SUBST(PYTHON)
+
# udev v190 introduced the btrfs builtin and a udev rule to use it.
# Our udev rule gives us the friendly dm names but isn't required (or valid)
# on earlier releases.
UDEVDIR=
-if $PKG_CONFIG udev --atleast-version 190; then
- UDEVDIR="$($PKG_CONFIG udev --variable=udevdir)"
+if ${PKG_CONFIG} udev --atleast-version 190; then
+ UDEVDIR="$(${PKG_CONFIG} udev --variable=udevdir)"
fi
AC_SUBST(UDEVDIR)
@@ -265,6 +285,8 @@ AC_MSG_RESULT([
backtrace support: ${enable_backtrace}
btrfs-convert: ${enable_convert} ${convertfs:+($convertfs)}
btrfs-restore zstd: ${enable_zstd}
+ Python bindings: ${enable_python}
+ Python interpreter: ${PYTHON}
Type 'make' to compile.
])
diff --git a/convert/common.c b/convert/common.c
index 3860f3b9..6ddf4a46 100644
--- a/convert/common.c
+++ b/convert/common.c
@@ -116,7 +116,7 @@ static int setup_temp_super(int fd, struct btrfs_mkfs_config *cfg,
btrfs_set_super_bytenr(super, cfg->super_bytenr);
btrfs_set_super_num_devices(super, 1);
- btrfs_set_super_magic(super, BTRFS_MAGIC_PARTIAL);
+ btrfs_set_super_magic(super, BTRFS_MAGIC_TEMPORARY);
btrfs_set_super_generation(super, 1);
btrfs_set_super_root(super, root_bytenr);
btrfs_set_super_chunk_root(super, chunk_bytenr);
@@ -219,7 +219,7 @@ static inline int write_temp_extent_buffer(int fd, struct extent_buffer *buf,
{
int ret;
- csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
+ csum_tree_block_size(buf, btrfs_csum_sizes[BTRFS_CSUM_TYPE_CRC32], 0);
/* Temporary extent buffer is always mapped 1:1 on disk */
ret = pwrite(fd, buf->data, buf->len, bytenr);
diff --git a/convert/main.c b/convert/main.c
index b3ea31d7..80f3bed8 100644
--- a/convert/main.c
+++ b/convert/main.c
@@ -1026,7 +1026,7 @@ static int migrate_super_block(int fd, u64 old_bytenr)
BUG_ON(btrfs_super_bytenr(super) != old_bytenr);
btrfs_set_super_bytenr(super, BTRFS_SUPER_INFO_OFFSET);
- csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
+ csum_tree_block_size(buf, btrfs_csum_sizes[BTRFS_CSUM_TYPE_CRC32], 0);
ret = pwrite(fd, buf->data, BTRFS_SUPER_INFO_SIZE,
BTRFS_SUPER_INFO_OFFSET);
if (ret != BTRFS_SUPER_INFO_SIZE)
@@ -1140,7 +1140,7 @@ static int do_convert(const char *devname, u32 convert_flags, u32 nodesize,
}
root = open_ctree_fd(fd, devname, mkfs_cfg.super_bytenr,
- OPEN_CTREE_WRITES | OPEN_CTREE_FS_PARTIAL);
+ OPEN_CTREE_WRITES | OPEN_CTREE_TEMPORARY_SUPER);
if (!root) {
error("unable to open ctree");
goto fail;
@@ -1230,7 +1230,7 @@ static int do_convert(const char *devname, u32 convert_flags, u32 nodesize,
}
root = open_ctree_fd(fd, devname, 0,
- OPEN_CTREE_WRITES | OPEN_CTREE_FS_PARTIAL);
+ OPEN_CTREE_WRITES | OPEN_CTREE_TEMPORARY_SUPER);
if (!root) {
error("unable to open ctree for finalization");
goto fail;
diff --git a/convert/source-ext2.c b/convert/source-ext2.c
index b1492c78..a2af1212 100644
--- a/convert/source-ext2.c
+++ b/convert/source-ext2.c
@@ -310,7 +310,7 @@ static int ext2_create_file_extents(struct btrfs_trans_handle *trans,
if (ret)
goto fail;
if ((convert_flags & CONVERT_FLAG_INLINE_DATA) && data.first_block == 0
- && data.num_blocks > 0
+ && data.num_blocks > 0 && inode_size < sectorsize
&& inode_size <= BTRFS_MAX_INLINE_DATA_SIZE(root->fs_info)) {
u64 num_bytes = data.num_blocks * sectorsize;
u64 disk_bytenr = data.disk_block * sectorsize;
@@ -422,8 +422,7 @@ static int ext2_xattr_check_entry(struct ext2_ext_attr_entry *entry,
{
size_t value_size = entry->e_value_size;
- if (entry->e_value_block != 0 || value_size > size ||
- entry->e_value_offs + value_size > size)
+ if (value_size > size || entry->e_value_offs + value_size > size)
return -EIO;
return 0;
}
diff --git a/convert/source-reiserfs.c b/convert/source-reiserfs.c
index 39d6f072..e0b3b685 100644
--- a/convert/source-reiserfs.c
+++ b/convert/source-reiserfs.c
@@ -350,8 +350,7 @@ static int convert_direct(struct btrfs_trans_handle *trans,
if (ret)
return ret;
- eb = alloc_extent_buffer(&root->fs_info->extent_cache, key.objectid,
- sectorsize);
+ eb = alloc_extent_buffer(root->fs_info, key.objectid, sectorsize);
if (!eb)
return -ENOMEM;
@@ -376,7 +375,8 @@ static int reiserfs_convert_tail(struct btrfs_trans_handle *trans,
u64 isize;
int ret;
- if (length >= BTRFS_MAX_INLINE_DATA_SIZE(root->fs_info))
+ if (length >= BTRFS_MAX_INLINE_DATA_SIZE(root->fs_info) ||
+ length >= root->fs_info->sectorsize)
return convert_direct(trans, root, objectid, inode, body,
length, offset, convert_flags);
diff --git a/ctree.c b/ctree.c
index 45b368ce..d1c41925 100644
--- a/ctree.c
+++ b/ctree.c
@@ -22,6 +22,7 @@
#include "repair.h"
#include "internal.h"
#include "sizes.h"
+#include "messages.h"
static int split_node(struct btrfs_trans_handle *trans, struct btrfs_root
*root, struct btrfs_path *path, int level);
@@ -479,11 +480,11 @@ btrfs_check_leaf(struct btrfs_root *root, struct btrfs_disk_key *parent_key,
(unsigned long long)btrfs_header_bytenr(buf));
goto fail;
}
- if (btrfs_leaf_free_space(root, buf) < 0) {
+ if (btrfs_leaf_free_space(root->fs_info, buf) < 0) {
ret = BTRFS_TREE_BLOCK_INVALID_FREE_SPACE;
fprintf(stderr, "leaf free space incorrect %llu %d\n",
(unsigned long long)btrfs_header_bytenr(buf),
- btrfs_leaf_free_space(root, buf));
+ btrfs_leaf_free_space(root->fs_info, buf));
goto fail;
}
@@ -642,7 +643,9 @@ static int bin_search(struct extent_buffer *eb, struct btrfs_key *key,
struct extent_buffer *read_node_slot(struct btrfs_fs_info *fs_info,
struct extent_buffer *parent, int slot)
{
+ struct extent_buffer *ret;
int level = btrfs_header_level(parent);
+
if (slot < 0)
return NULL;
if (slot >= btrfs_header_nritems(parent))
@@ -651,8 +654,20 @@ struct extent_buffer *read_node_slot(struct btrfs_fs_info *fs_info,
if (level == 0)
return NULL;
- return read_tree_block(fs_info, btrfs_node_blockptr(parent, slot),
+ ret = read_tree_block(fs_info, btrfs_node_blockptr(parent, slot),
btrfs_node_ptr_generation(parent, slot));
+ if (!extent_buffer_uptodate(ret))
+ return ERR_PTR(-EIO);
+
+ if (btrfs_header_level(ret) != level - 1) {
+ error(
+"child eb corrupted: parent bytenr=%llu item=%d parent level=%d child level=%d",
+ btrfs_header_bytenr(parent), slot,
+ btrfs_header_level(parent), btrfs_header_level(ret));
+ free_extent_buffer(ret);
+ return ERR_PTR(-EIO);
+ }
+ return ret;
}
static int balance_level(struct btrfs_trans_handle *trans,
@@ -1178,7 +1193,7 @@ again:
} else {
p->slots[level] = slot;
if (ins_len > 0 &&
- ins_len > btrfs_leaf_free_space(root, b)) {
+ ins_len > btrfs_leaf_free_space(root->fs_info, b)) {
int sret = split_leaf(trans, root, key,
p, ins_len, ret == 0);
BUG_ON(sret > 0);
@@ -1619,16 +1634,17 @@ static int leaf_space_used(struct extent_buffer *l, int start, int nr)
* the start of the leaf data. IOW, how much room
* the leaf has left for both items and data
*/
-int btrfs_leaf_free_space(struct btrfs_root *root, struct extent_buffer *leaf)
+int btrfs_leaf_free_space(struct btrfs_fs_info *fs_info,
+ struct extent_buffer *leaf)
{
- u32 nodesize = (root ? BTRFS_LEAF_DATA_SIZE(root->fs_info) : leaf->len);
int nritems = btrfs_header_nritems(leaf);
int ret;
- ret = nodesize - leaf_space_used(leaf, 0, nritems);
+
+ ret = BTRFS_LEAF_DATA_SIZE(fs_info) - leaf_space_used(leaf, 0, nritems);
if (ret < 0) {
- printk("leaf free space ret %d, leaf data size %u, used %d nritems %d\n",
- ret, nodesize, leaf_space_used(leaf, 0, nritems),
- nritems);
+ printk("leaf free space ret %d, leaf data size %lu, used %d nritems %d\n",
+ ret, BTRFS_LEAF_DATA_SIZE(fs_info),
+ leaf_space_used(leaf, 0, nritems), nritems);
}
return ret;
}
@@ -1676,7 +1692,7 @@ static int push_leaf_right(struct btrfs_trans_handle *trans, struct btrfs_root
return PTR_ERR(right);
return -EIO;
}
- free_space = btrfs_leaf_free_space(root, right);
+ free_space = btrfs_leaf_free_space(fs_info, right);
if (free_space < data_size) {
free_extent_buffer(right);
return 1;
@@ -1689,7 +1705,7 @@ static int push_leaf_right(struct btrfs_trans_handle *trans, struct btrfs_root
free_extent_buffer(right);
return 1;
}
- free_space = btrfs_leaf_free_space(root, right);
+ free_space = btrfs_leaf_free_space(fs_info, right);
if (free_space < data_size) {
free_extent_buffer(right);
return 1;
@@ -1828,7 +1844,7 @@ static int push_leaf_left(struct btrfs_trans_handle *trans, struct btrfs_root
}
left = read_node_slot(fs_info, path->nodes[1], slot - 1);
- free_space = btrfs_leaf_free_space(root, left);
+ free_space = btrfs_leaf_free_space(fs_info, left);
if (free_space < data_size) {
free_extent_buffer(left);
return 1;
@@ -1843,7 +1859,7 @@ static int push_leaf_left(struct btrfs_trans_handle *trans, struct btrfs_root
return 1;
}
- free_space = btrfs_leaf_free_space(root, left);
+ free_space = btrfs_leaf_free_space(fs_info, left);
if (free_space < data_size) {
free_extent_buffer(left);
return 1;
@@ -2067,7 +2083,7 @@ static noinline int split_leaf(struct btrfs_trans_handle *trans,
l = path->nodes[0];
/* did the pushes work? */
- if (btrfs_leaf_free_space(root, l) >= data_size)
+ if (btrfs_leaf_free_space(root->fs_info, l) >= data_size)
return 0;
}
@@ -2224,7 +2240,8 @@ int btrfs_split_item(struct btrfs_trans_handle *trans,
leaf = path->nodes[0];
btrfs_item_key_to_cpu(leaf, &orig_key, path->slots[0]);
- if (btrfs_leaf_free_space(root, leaf) >= sizeof(struct btrfs_item))
+ if (btrfs_leaf_free_space(root->fs_info, leaf) >=
+ sizeof(struct btrfs_item))
goto split;
item_size = btrfs_item_size_nr(leaf, path->slots[0]);
@@ -2244,7 +2261,8 @@ int btrfs_split_item(struct btrfs_trans_handle *trans,
ret = split_leaf(trans, root, &orig_key, path, 0, 0);
BUG_ON(ret);
- BUG_ON(btrfs_leaf_free_space(root, leaf) < sizeof(struct btrfs_item));
+ BUG_ON(btrfs_leaf_free_space(root->fs_info, leaf) <
+ sizeof(struct btrfs_item));
leaf = path->nodes[0];
split:
@@ -2296,8 +2314,8 @@ split:
btrfs_mark_buffer_dirty(leaf);
ret = 0;
- if (btrfs_leaf_free_space(root, leaf) < 0) {
- btrfs_print_leaf(root, leaf);
+ if (btrfs_leaf_free_space(root->fs_info, leaf) < 0) {
+ btrfs_print_leaf(leaf);
BUG();
}
kfree(buf);
@@ -2392,8 +2410,8 @@ int btrfs_truncate_item(struct btrfs_root *root, struct btrfs_path *path,
btrfs_mark_buffer_dirty(leaf);
ret = 0;
- if (btrfs_leaf_free_space(root, leaf) < 0) {
- btrfs_print_leaf(root, leaf);
+ if (btrfs_leaf_free_space(root->fs_info, leaf) < 0) {
+ btrfs_print_leaf(leaf);
BUG();
}
return ret;
@@ -2417,8 +2435,8 @@ int btrfs_extend_item(struct btrfs_root *root, struct btrfs_path *path,
nritems = btrfs_header_nritems(leaf);
data_end = leaf_data_end(root->fs_info, leaf);
- if (btrfs_leaf_free_space(root, leaf) < data_size) {
- btrfs_print_leaf(root, leaf);
+ if (btrfs_leaf_free_space(root->fs_info, leaf) < data_size) {
+ btrfs_print_leaf(leaf);
BUG();
}
slot = path->slots[0];
@@ -2426,7 +2444,7 @@ int btrfs_extend_item(struct btrfs_root *root, struct btrfs_path *path,
BUG_ON(slot < 0);
if (slot >= nritems) {
- btrfs_print_leaf(root, leaf);
+ btrfs_print_leaf(leaf);
printk("slot %d too large, nritems %d\n", slot, nritems);
BUG_ON(1);
}
@@ -2454,8 +2472,8 @@ int btrfs_extend_item(struct btrfs_root *root, struct btrfs_path *path,
btrfs_mark_buffer_dirty(leaf);
ret = 0;
- if (btrfs_leaf_free_space(root, leaf) < 0) {
- btrfs_print_leaf(root, leaf);
+ if (btrfs_leaf_free_space(root->fs_info, leaf) < 0) {
+ btrfs_print_leaf(leaf);
BUG();
}
return ret;
@@ -2503,10 +2521,10 @@ int btrfs_insert_empty_items(struct btrfs_trans_handle *trans,
nritems = btrfs_header_nritems(leaf);
data_end = leaf_data_end(root->fs_info, leaf);
- if (btrfs_leaf_free_space(root, leaf) < total_size) {
- btrfs_print_leaf(root, leaf);
+ if (btrfs_leaf_free_space(root->fs_info, leaf) < total_size) {
+ btrfs_print_leaf(leaf);
printk("not enough freespace need %u have %d\n",
- total_size, btrfs_leaf_free_space(root, leaf));
+ total_size, btrfs_leaf_free_space(root->fs_info, leaf));
BUG();
}
@@ -2517,7 +2535,7 @@ int btrfs_insert_empty_items(struct btrfs_trans_handle *trans,
unsigned int old_data = btrfs_item_end_nr(leaf, slot);
if (old_data < data_end) {
- btrfs_print_leaf(root, leaf);
+ btrfs_print_leaf(leaf);
printk("slot %d old_data %d data_end %d\n",
slot, old_data, data_end);
BUG_ON(1);
@@ -2564,8 +2582,8 @@ int btrfs_insert_empty_items(struct btrfs_trans_handle *trans,
btrfs_fixup_low_keys(root, path, &disk_key, 1);
}
- if (btrfs_leaf_free_space(root, leaf) < 0) {
- btrfs_print_leaf(root, leaf);
+ if (btrfs_leaf_free_space(root->fs_info, leaf) < 0) {
+ btrfs_print_leaf(leaf);
BUG();
}
diff --git a/ctree.h b/ctree.h
index 17cdac76..1fef37c9 100644
--- a/ctree.h
+++ b/ctree.h
@@ -45,10 +45,12 @@ struct btrfs_free_space_ctl;
#define BTRFS_MAGIC 0x4D5F53665248425FULL /* ascii _BHRfS_M, no null */
/*
- * Fake signature for an unfinalized filesystem, structures might be partially
- * created or missing.
+ * Fake signature for an unfinalized filesystem, which only has barebone tree
+ * structures (normally 6 near empty trees, on SINGLE meta/sys temporary chunks)
+ *
+ * ascii !BHRfS_M, no null
*/
-#define BTRFS_MAGIC_PARTIAL 0x4D5F536652484221ULL /* ascii !BHRfS_M, no null */
+#define BTRFS_MAGIC_TEMPORARY 0x4D5F536652484221ULL
#define BTRFS_MAX_MIRRORS 3
@@ -167,10 +169,9 @@ struct btrfs_free_space_ctl;
/* csum types */
#define BTRFS_CSUM_TYPE_CRC32 0
+/* four bytes for CRC32 */
static int btrfs_csum_sizes[] = { 4 };
-/* four bytes for CRC32 */
-#define BTRFS_CRC32_SIZE 4
#define BTRFS_EMPTY_DIR_SIZE 0
#define BTRFS_FT_UNKNOWN 0
@@ -958,7 +959,17 @@ struct btrfs_csum_item {
#define BTRFS_BLOCK_GROUP_RAID5 (1ULL << 7)
#define BTRFS_BLOCK_GROUP_RAID6 (1ULL << 8)
#define BTRFS_BLOCK_GROUP_RESERVED BTRFS_AVAIL_ALLOC_BIT_SINGLE
-#define BTRFS_NR_RAID_TYPES 7
+
+enum btrfs_raid_types {
+ BTRFS_RAID_RAID10,
+ BTRFS_RAID_RAID1,
+ BTRFS_RAID_DUP,
+ BTRFS_RAID_RAID0,
+ BTRFS_RAID_SINGLE,
+ BTRFS_RAID_RAID5,
+ BTRFS_RAID_RAID6,
+ BTRFS_NR_RAID_TYPES
+};
#define BTRFS_BLOCK_GROUP_TYPE_MASK (BTRFS_BLOCK_GROUP_DATA | \
BTRFS_BLOCK_GROUP_SYSTEM | \
@@ -2653,7 +2664,8 @@ static inline int btrfs_next_item(struct btrfs_root *root,
}
int btrfs_prev_leaf(struct btrfs_root *root, struct btrfs_path *path);
-int btrfs_leaf_free_space(struct btrfs_root *root, struct extent_buffer *leaf);
+int btrfs_leaf_free_space(struct btrfs_fs_info *fs_info,
+ struct extent_buffer *leaf);
void btrfs_fixup_low_keys(struct btrfs_root *root, struct btrfs_path *path,
struct btrfs_disk_key *key, int level);
int btrfs_set_item_key_safe(struct btrfs_root *root, struct btrfs_path *path,
diff --git a/debian/changelog b/debian/changelog
index 65be5d62..f7546403 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+btrfs-progs (4.16.1-1) unstable; urgency=medium
+
+ * New upstream release.
+
+ -- Dimitri John Ledkov <xnox@ubuntu.com> Tue, 08 May 2018 14:17:03 -0700
+
btrfs-progs (4.15.1-2) unstable; urgency=medium
* If libzstd is provided in both deb & udeb variants (such as Ubuntu
diff --git a/disk-io.c b/disk-io.c
index 76958aef..72d44531 100644
--- a/disk-io.c
+++ b/disk-io.c
@@ -184,8 +184,7 @@ struct extent_buffer *btrfs_find_tree_block(struct btrfs_fs_info *fs_info,
struct extent_buffer* btrfs_find_create_tree_block(
struct btrfs_fs_info *fs_info, u64 bytenr)
{
- return alloc_extent_buffer(&fs_info->extent_cache, bytenr,
- fs_info->nodesize);
+ return alloc_extent_buffer(fs_info, bytenr, fs_info->nodesize);
}
void readahead_tree_block(struct btrfs_fs_info *fs_info, u64 bytenr,
@@ -396,10 +395,12 @@ int read_extent_data(struct btrfs_fs_info *fs_info, char *data, u64 logical,
}
device = multi->stripes[0].dev;
- if (device->fd <= 0)
- goto err;
if (*len > max_len)
*len = max_len;
+ if (device->fd < 0) {
+ ret = -EIO;
+ goto err;
+ }
ret = pread64(device->fd, data, *len, multi->stripes[0].physical);
if (ret != *len)
@@ -1115,14 +1116,14 @@ static struct btrfs_fs_info *__open_ctree_fd(int fp, const char *path,
fs_info->ignore_chunk_tree_error = 1;
if ((flags & OPEN_CTREE_RECOVER_SUPER)
- && (flags & OPEN_CTREE_FS_PARTIAL)) {
+ && (flags & OPEN_CTREE_TEMPORARY_SUPER)) {
fprintf(stderr,
- "cannot open a partially created filesystem for recovery");
+ "cannot open a filesystem with temporary super block for recovery");
goto out;
}
- if (flags & OPEN_CTREE_FS_PARTIAL)
- sbflags = SBREAD_PARTIAL;
+ if (flags & OPEN_CTREE_TEMPORARY_SUPER)
+ sbflags = SBREAD_TEMPORARY;
ret = btrfs_scan_fs_devices(fp, path, &fs_devices, sb_bytenr, sbflags,
(flags & OPEN_CTREE_NO_DEVICES));
@@ -1283,8 +1284,8 @@ static int check_super(struct btrfs_super_block *sb, unsigned sbflags)
int csum_size;
if (btrfs_super_magic(sb) != BTRFS_MAGIC) {
- if (btrfs_super_magic(sb) == BTRFS_MAGIC_PARTIAL) {
- if (!(sbflags & SBREAD_PARTIAL)) {
+ if (btrfs_super_magic(sb) == BTRFS_MAGIC_TEMPORARY) {
+ if (!(sbflags & SBREAD_TEMPORARY)) {
error("superblock magic doesn't match");
return -EIO;
}
diff --git a/disk-io.h b/disk-io.h
index f6a422f2..c4496155 100644
--- a/disk-io.h
+++ b/disk-io.h
@@ -73,8 +73,12 @@ enum btrfs_open_ctree_flags {
*/
OPEN_CTREE_IGNORE_CHUNK_TREE_ERROR = (1U << 11),
- /* Allow to open a partially created filesystem */
- OPEN_CTREE_FS_PARTIAL = (1U << 12),
+ /*
+ * Allow to open fs with temporary superblock (BTRFS_MAGIC_PARTIAL),
+ * such fs contains very basic tree layout, just able to be opened.
+ * Such temporary super is used for mkfs or convert.
+ */
+ OPEN_CTREE_TEMPORARY_SUPER = (1U << 12),
/*
* Invalidate the free space tree (i.e., clear the FREE_SPACE_TREE_VALID
@@ -95,7 +99,7 @@ enum btrfs_read_sb_flags {
* Read superblock with the fake signature, cannot be used with
* SBREAD_RECOVER
*/
- SBREAD_PARTIAL = (1 << 1),
+ SBREAD_TEMPORARY = (1 << 1),
};
/*
diff --git a/extent-tree.c b/extent-tree.c
index e2ae74a7..ea205ccf 100644
--- a/extent-tree.c
+++ b/extent-tree.c
@@ -1070,7 +1070,7 @@ again:
printf("Size is %u, needs to be %u, slot %d\n",
(unsigned)item_size,
(unsigned)sizeof(*ei), path->slots[0]);
- btrfs_print_leaf(root, leaf);
+ btrfs_print_leaf(leaf);
return -EINVAL;
}
BUG_ON(item_size < sizeof(*ei));
@@ -1587,7 +1587,7 @@ again:
}
if (ret != 0) {
- btrfs_print_leaf(root, path->nodes[0]);
+ btrfs_print_leaf(path->nodes[0]);
printk("failed to find block number %Lu\n",
(unsigned long long)bytenr);
BUG();
@@ -2273,7 +2273,7 @@ static int __free_extent(struct btrfs_trans_handle *trans,
printk(KERN_ERR "umm, got %d back from search"
", was looking for %llu\n", ret,
(unsigned long long)bytenr);
- btrfs_print_leaf(extent_root, path->nodes[0]);
+ btrfs_print_leaf(path->nodes[0]);
}
BUG_ON(ret);
extent_slot = path->slots[0];
@@ -2311,7 +2311,7 @@ static int __free_extent(struct btrfs_trans_handle *trans,
printk(KERN_ERR "umm, got %d back from search"
", was looking for %llu\n", ret,
(unsigned long long)bytenr);
- btrfs_print_leaf(extent_root, path->nodes[0]);
+ btrfs_print_leaf(path->nodes[0]);
}
BUG_ON(ret);
extent_slot = path->slots[0];
diff --git a/extent_io.c b/extent_io.c
index eda1fb6f..19849269 100644
--- a/extent_io.c
+++ b/extent_io.c
@@ -545,7 +545,7 @@ out:
return ret;
}
-static struct extent_buffer *__alloc_extent_buffer(struct extent_io_tree *tree,
+static struct extent_buffer *__alloc_extent_buffer(struct btrfs_fs_info *info,
u64 bytenr, u32 blocksize)
{
struct extent_buffer *eb;
@@ -558,12 +558,14 @@ static struct extent_buffer *__alloc_extent_buffer(struct extent_io_tree *tree,
eb->len = blocksize;
eb->refs = 1;
eb->flags = 0;
- eb->tree = tree;
eb->fd = -1;
eb->dev_bytenr = (u64)-1;
eb->cache_node.start = bytenr;
eb->cache_node.size = blocksize;
+ eb->fs_info = info;
+ eb->tree = &info->extent_cache;
INIT_LIST_HEAD(&eb->recow);
+ INIT_LIST_HEAD(&eb->lru);
return eb;
}
@@ -572,9 +574,11 @@ struct extent_buffer *btrfs_clone_extent_buffer(struct extent_buffer *src)
{
struct extent_buffer *new;
- new = __alloc_extent_buffer(NULL, src->start, src->len);
+ new = __alloc_extent_buffer(src->fs_info, src->start, src->len);
if (!new)
return NULL;
+ /* cloned eb is not linked into fs_info->extent_cache */
+ new->tree = NULL;
copy_extent_buffer(new, src, 0, 0, src->len);
new->flags |= EXTENT_BUFFER_DUMMY;
@@ -587,7 +591,7 @@ static void free_extent_buffer_final(struct extent_buffer *eb)
struct extent_io_tree *tree = eb->tree;
BUG_ON(eb->refs);
- BUG_ON(tree->cache_size < eb->len);
+ BUG_ON(tree && tree->cache_size < eb->len);
list_del_init(&eb->lru);
if (!(eb->flags & EXTENT_BUFFER_DUMMY)) {
remove_cache_extent(&tree->cache, &eb->cache_node);
@@ -664,10 +668,11 @@ static void trim_extent_buffer_cache(struct extent_io_tree *tree)
}
}
-struct extent_buffer *alloc_extent_buffer(struct extent_io_tree *tree,
+struct extent_buffer *alloc_extent_buffer(struct btrfs_fs_info *fs_info,
u64 bytenr, u32 blocksize)
{
struct extent_buffer *eb;
+ struct extent_io_tree *tree = &fs_info->extent_cache;
struct cache_extent *cache;
cache = lookup_cache_extent(&tree->cache, bytenr, blocksize);
@@ -684,7 +689,7 @@ struct extent_buffer *alloc_extent_buffer(struct extent_io_tree *tree,
cache_node);
free_extent_buffer(eb);
}
- eb = __alloc_extent_buffer(tree, bytenr, blocksize);
+ eb = __alloc_extent_buffer(fs_info, bytenr, blocksize);
if (!eb)
return NULL;
ret = insert_cache_extent(&tree->cache, &eb->cache_node);
diff --git a/extent_io.h b/extent_io.h
index 17a4a829..d407d93d 100644
--- a/extent_io.h
+++ b/extent_io.h
@@ -91,13 +91,14 @@ struct extent_buffer {
struct cache_extent cache_node;
u64 start;
u64 dev_bytenr;
- u32 len;
struct extent_io_tree *tree;
struct list_head lru;
struct list_head recow;
+ u32 len;
int refs;
u32 flags;
int fd;
+ struct btrfs_fs_info *fs_info;
char data[] __attribute__((aligned(8)));
};
@@ -145,7 +146,7 @@ struct extent_buffer *find_extent_buffer(struct extent_io_tree *tree,
u64 bytenr, u32 blocksize);
struct extent_buffer *find_first_extent_buffer(struct extent_io_tree *tree,
u64 start);
-struct extent_buffer *alloc_extent_buffer(struct extent_io_tree *tree,
+struct extent_buffer *alloc_extent_buffer(struct btrfs_fs_info *fs_info,
u64 bytenr, u32 blocksize);
struct extent_buffer *btrfs_clone_extent_buffer(struct extent_buffer *src);
void free_extent_buffer(struct extent_buffer *eb);
diff --git a/free-space-cache.c b/free-space-cache.c
index 50356d04..9b83a71c 100644
--- a/free-space-cache.c
+++ b/free-space-cache.c
@@ -54,8 +54,7 @@ static int io_ctl_init(struct io_ctl *io_ctl, u64 size, u64 ino,
struct btrfs_root *root)
{
memset(io_ctl, 0, sizeof(struct io_ctl));
- io_ctl->num_pages = (size + root->fs_info->sectorsize - 1) /
- root->fs_info->sectorsize;
+ io_ctl->num_pages = DIV_ROUND_UP(size, root->fs_info->sectorsize);
io_ctl->buffer = kzalloc(size, GFP_NOFS);
if (!io_ctl->buffer)
return -ENOMEM;
@@ -439,7 +438,8 @@ int load_free_space_cache(struct btrfs_fs_info *fs_info,
struct btrfs_path *path;
u64 used = btrfs_block_group_used(&block_group->item);
int ret = 0;
- int matched;
+ u64 bg_free;
+ s64 diff;
path = btrfs_alloc_path();
if (!path)
@@ -449,18 +449,33 @@ int load_free_space_cache(struct btrfs_fs_info *fs_info,
block_group->key.objectid);
btrfs_free_path(path);
- matched = (ctl->free_space == (block_group->key.offset - used -
- block_group->bytes_super));
- if (ret == 1 && !matched) {
- __btrfs_remove_free_space_cache(ctl);
+ bg_free = block_group->key.offset - used - block_group->bytes_super;
+ diff = ctl->free_space - bg_free;
+ if (ret == 1 && diff) {
fprintf(stderr,
- "block group %llu has wrong amount of free space\n",
- block_group->key.objectid);
+ "block group %llu has wrong amount of free space, free space cache has %llu block group has %llu\n",
+ block_group->key.objectid, ctl->free_space, bg_free);
+ __btrfs_remove_free_space_cache(ctl);
+ /*
+ * Due to btrfs_reserve_extent() can happen out of a
+ * transaction, but all btrfs_release_extent() happens inside
+ * a transaction, so under heavy race it's possible that free
+ * space cache has less free space, and both kernel just discard
+ * such cache. But if we find some case where free space cache
+ * has more free space, this means under certain case such
+ * cache can be loaded and cause double allocate.
+ *
+ * Detect such possibility here.
+ */
+ if (diff > 0)
+ error(
+"free space cache has more free space than block group item, this could leads to serious corruption, please contact btrfs developers");
ret = -1;
}
if (ret < 0) {
- ret = 0;
+ if (diff <= 0)
+ ret = 0;
fprintf(stderr,
"failed to load free space cache for block group %llu\n",
diff --git a/help.c b/help.c
index 311a4320..f1dd3946 100644
--- a/help.c
+++ b/help.c
@@ -115,7 +115,7 @@ void clean_args_no_options(int argc, char *argv[], const char * const *usagestr)
* - "-- option1 option2 ..."
* - "option1 option2 ..."
*/
-void clean_args_no_options_relaxed(int argc, char *argv[], const char * const *usagestr)
+void clean_args_no_options_relaxed(int argc, char *argv[])
{
if (argc <= 1)
return;
diff --git a/help.h b/help.h
index efeded30..a1405942 100644
--- a/help.h
+++ b/help.h
@@ -72,8 +72,7 @@ int check_argc_exact(int nargs, int expected);
int check_argc_min(int nargs, int expected);
int check_argc_max(int nargs, int expected);
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);
+void clean_args_no_options_relaxed(int argc, char *argv[]);
void fixup_argv0(char **argv, const char *token);
void set_argv0(char **argv);
diff --git a/image/main.c b/image/main.c
index 9c75c8b4..351c5a25 100644
--- a/image/main.c
+++ b/image/main.c
@@ -119,11 +119,11 @@ static struct extent_buffer *alloc_dummy_eb(u64 bytenr, u32 size);
static void csum_block(u8 *buf, size_t len)
{
- u8 result[BTRFS_CRC32_SIZE];
+ u8 result[btrfs_csum_sizes[BTRFS_CSUM_TYPE_CRC32]];
u32 crc = ~(u32)0;
crc = crc32c(crc, buf + BTRFS_CSUM_SIZE, len - BTRFS_CSUM_SIZE);
btrfs_csum_final(crc, result);
- memcpy(buf, result, BTRFS_CRC32_SIZE);
+ memcpy(buf, result, btrfs_csum_sizes[BTRFS_CSUM_TYPE_CRC32]);
}
static int has_name(struct btrfs_key *key)
@@ -571,7 +571,7 @@ static int read_data_extent(struct metadump_struct *md,
num_copies = btrfs_num_copies(root->fs_info, logical, bytes_left);
/* Try our best to read data, just like read_tree_block() */
- for (cur_mirror = 0; cur_mirror < num_copies; cur_mirror++) {
+ for (cur_mirror = 1; cur_mirror <= num_copies; cur_mirror++) {
while (bytes_left) {
read_len = bytes_left;
ret = read_extent_data(fs_info,
diff --git a/libbtrfsutil/COPYING b/libbtrfsutil/COPYING
new file mode 100644
index 00000000..f288702d
--- /dev/null
+++ b/libbtrfsutil/COPYING
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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 <https://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<https://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<https://www.gnu.org/licenses/why-not-lgpl.html>.
diff --git a/libbtrfsutil/COPYING.LESSER b/libbtrfsutil/COPYING.LESSER
new file mode 100644
index 00000000..0a041280
--- /dev/null
+++ b/libbtrfsutil/COPYING.LESSER
@@ -0,0 +1,165 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/libbtrfsutil/README.md b/libbtrfsutil/README.md
new file mode 100644
index 00000000..0c8eba44
--- /dev/null
+++ b/libbtrfsutil/README.md
@@ -0,0 +1,38 @@
+libbtrfsutil
+============
+
+libbtrfsutil is a library for managing Btrfs filesystems. It is licensed under
+the LGPL. libbtrfsutil provides interfaces for a subset of the operations
+offered by the `btrfs` command line utility. It also includes official Python
+bindings (Python 3 only).
+
+Development
+-----------
+
+The [development process for btrfs-progs](../README.md#development) applies.
+
+libbtrfsutil only includes operations that are done through the filesystem and
+ioctl interface, not operations that modify the filesystem directly (e.g., mkfs
+or fsck). This is by design but also a legal necessity, as the filesystem
+implementation is GPL but libbtrfsutil is LGPL. That is also why the
+libbtrfsutil code is a reimplementation of the btrfs-progs code rather than a
+refactoring. Be wary of this when porting functionality.
+
+libbtrfsutil is semantically versioned separately from btrfs-progs. It is the
+maintainers' responsibility to bump the version as needed (at most once per
+release of btrfs-progs).
+
+A few guidelines:
+
+* All interfaces must be documented in `btrfsutil.h` using the kernel-doc style
+* Error codes should be specific about what _exactly_ failed
+* Functions should have a path and an fd variant whenever possible
+* Spell out terms in function names, etc. rather than abbreviating whenever
+ possible
+* Don't require the Btrfs UAPI headers for any interfaces (e.g., instead of
+ directly exposing a type from `linux/btrfs_tree.h`, abstract it away in a
+ type specific to `libbtrfsutil`)
+* Preserve API and ABI compatability at all times (i.e., we don't want to bump
+ the library major version if we don't have to)
+* Include Python bindings for all interfaces
+* Write tests for all interfaces
diff --git a/libbtrfsutil/btrfs.h b/libbtrfsutil/btrfs.h
new file mode 100644
index 00000000..c293f6bf
--- /dev/null
+++ b/libbtrfsutil/btrfs.h
@@ -0,0 +1,840 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Copyright (C) 2007 Oracle. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#ifndef _LINUX_BTRFS_H
+#define _LINUX_BTRFS_H
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+#define BTRFS_IOCTL_MAGIC 0x94
+#define BTRFS_VOL_NAME_MAX 255
+#define BTRFS_LABEL_SIZE 256
+
+/* this should be 4k */
+#define BTRFS_PATH_NAME_MAX 4087
+struct btrfs_ioctl_vol_args {
+ __s64 fd;
+ char name[BTRFS_PATH_NAME_MAX + 1];
+};
+
+#define BTRFS_DEVICE_PATH_NAME_MAX 1024
+
+#define BTRFS_DEVICE_SPEC_BY_ID (1ULL << 3)
+
+#define BTRFS_VOL_ARG_V2_FLAGS_SUPPORTED \
+ (BTRFS_SUBVOL_CREATE_ASYNC | \
+ BTRFS_SUBVOL_RDONLY | \
+ BTRFS_SUBVOL_QGROUP_INHERIT | \
+ BTRFS_DEVICE_SPEC_BY_ID)
+
+#define BTRFS_FSID_SIZE 16
+#define BTRFS_UUID_SIZE 16
+#define BTRFS_UUID_UNPARSED_SIZE 37
+
+/*
+ * flags definition for qgroup limits
+ *
+ * Used by:
+ * struct btrfs_qgroup_limit.flags
+ * struct btrfs_qgroup_limit_item.flags
+ */
+#define BTRFS_QGROUP_LIMIT_MAX_RFER (1ULL << 0)
+#define BTRFS_QGROUP_LIMIT_MAX_EXCL (1ULL << 1)
+#define BTRFS_QGROUP_LIMIT_RSV_RFER (1ULL << 2)
+#define BTRFS_QGROUP_LIMIT_RSV_EXCL (1ULL << 3)
+#define BTRFS_QGROUP_LIMIT_RFER_CMPR (1ULL << 4)
+#define BTRFS_QGROUP_LIMIT_EXCL_CMPR (1ULL << 5)
+
+struct btrfs_qgroup_limit {
+ __u64 flags;
+ __u64 max_rfer;
+ __u64 max_excl;
+ __u64 rsv_rfer;
+ __u64 rsv_excl;
+};
+
+/*
+ * flags definition for qgroup inheritance
+ *
+ * Used by:
+ * struct btrfs_qgroup_inherit.flags
+ */
+#define BTRFS_QGROUP_INHERIT_SET_LIMITS (1ULL << 0)
+
+struct btrfs_qgroup_inherit {
+ __u64 flags;
+ __u64 num_qgroups;
+ __u64 num_ref_copies;
+ __u64 num_excl_copies;
+ struct btrfs_qgroup_limit lim;
+ __u64 qgroups[0];
+};
+
+struct btrfs_ioctl_qgroup_limit_args {
+ __u64 qgroupid;
+ struct btrfs_qgroup_limit lim;
+};
+
+/*
+ * flags for subvolumes
+ *
+ * Used by:
+ * struct btrfs_ioctl_vol_args_v2.flags
+ *
+ * BTRFS_SUBVOL_RDONLY is also provided/consumed by the following ioctls:
+ * - BTRFS_IOC_SUBVOL_GETFLAGS
+ * - BTRFS_IOC_SUBVOL_SETFLAGS
+ */
+#define BTRFS_SUBVOL_CREATE_ASYNC (1ULL << 0)
+#define BTRFS_SUBVOL_RDONLY (1ULL << 1)
+#define BTRFS_SUBVOL_QGROUP_INHERIT (1ULL << 2)
+
+#define BTRFS_SUBVOL_NAME_MAX 4039
+struct btrfs_ioctl_vol_args_v2 {
+ __s64 fd;
+ __u64 transid;
+ __u64 flags;
+ union {
+ struct {
+ __u64 size;
+ struct btrfs_qgroup_inherit *qgroup_inherit;
+ };
+ __u64 unused[4];
+ };
+ union {
+ char name[BTRFS_SUBVOL_NAME_MAX + 1];
+ __u64 devid;
+ };
+};
+
+/*
+ * structure to report errors and progress to userspace, either as a
+ * result of a finished scrub, a canceled scrub or a progress inquiry
+ */
+struct btrfs_scrub_progress {
+ __u64 data_extents_scrubbed; /* # of data extents scrubbed */
+ __u64 tree_extents_scrubbed; /* # of tree extents scrubbed */
+ __u64 data_bytes_scrubbed; /* # of data bytes scrubbed */
+ __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
+ * of a tree block did not match the
+ * expected values, like generation or
+ * logical */
+ __u64 no_csum; /* # of 4k data block for which no csum
+ * is present, probably the result of
+ * data written with nodatasum */
+ __u64 csum_discards; /* # of csum for which no data was found
+ * in the extent tree. */
+ __u64 super_errors; /* # of bad super blocks encountered */
+ __u64 malloc_errors; /* # of internal kmalloc errors. These
+ * will likely cause an incomplete
+ * scrub */
+ __u64 uncorrectable_errors; /* # of errors where either no intact
+ * copy was found or the writeback
+ * failed */
+ __u64 corrected_errors; /* # of errors corrected */
+ __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
+ * full (64k) bio failed, but the re-
+ * check succeeded for each 4k piece.
+ * Intermittent error. */
+};
+
+#define BTRFS_SCRUB_READONLY 1
+struct btrfs_ioctl_scrub_args {
+ __u64 devid; /* in */
+ __u64 start; /* in */
+ __u64 end; /* in */
+ __u64 flags; /* in */
+ struct btrfs_scrub_progress progress; /* out */
+ /* pad to 1k */
+ __u64 unused[(1024-32-sizeof(struct btrfs_scrub_progress))/8];
+};
+
+#define BTRFS_IOCTL_DEV_REPLACE_CONT_READING_FROM_SRCDEV_MODE_ALWAYS 0
+#define BTRFS_IOCTL_DEV_REPLACE_CONT_READING_FROM_SRCDEV_MODE_AVOID 1
+struct btrfs_ioctl_dev_replace_start_params {
+ __u64 srcdevid; /* in, if 0, use srcdev_name instead */
+ __u64 cont_reading_from_srcdev_mode; /* in, see #define
+ * above */
+ __u8 srcdev_name[BTRFS_DEVICE_PATH_NAME_MAX + 1]; /* in */
+ __u8 tgtdev_name[BTRFS_DEVICE_PATH_NAME_MAX + 1]; /* in */
+};
+
+#define BTRFS_IOCTL_DEV_REPLACE_STATE_NEVER_STARTED 0
+#define BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED 1
+#define BTRFS_IOCTL_DEV_REPLACE_STATE_FINISHED 2
+#define BTRFS_IOCTL_DEV_REPLACE_STATE_CANCELED 3
+#define BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED 4
+struct btrfs_ioctl_dev_replace_status_params {
+ __u64 replace_state; /* out, see #define above */
+ __u64 progress_1000; /* out, 0 <= x <= 1000 */
+ __u64 time_started; /* out, seconds since 1-Jan-1970 */
+ __u64 time_stopped; /* out, seconds since 1-Jan-1970 */
+ __u64 num_write_errors; /* out */
+ __u64 num_uncorrectable_read_errors; /* out */
+};
+
+#define BTRFS_IOCTL_DEV_REPLACE_CMD_START 0
+#define BTRFS_IOCTL_DEV_REPLACE_CMD_STATUS 1
+#define BTRFS_IOCTL_DEV_REPLACE_CMD_CANCEL 2
+#define BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR 0
+#define BTRFS_IOCTL_DEV_REPLACE_RESULT_NOT_STARTED 1
+#define BTRFS_IOCTL_DEV_REPLACE_RESULT_ALREADY_STARTED 2
+#define BTRFS_IOCTL_DEV_REPLACE_RESULT_SCRUB_INPROGRESS 3
+struct btrfs_ioctl_dev_replace_args {
+ __u64 cmd; /* in */
+ __u64 result; /* out */
+
+ union {
+ struct btrfs_ioctl_dev_replace_start_params start;
+ struct btrfs_ioctl_dev_replace_status_params status;
+ }; /* in/out */
+
+ __u64 spare[64];
+};
+
+struct btrfs_ioctl_dev_info_args {
+ __u64 devid; /* in/out */
+ __u8 uuid[BTRFS_UUID_SIZE]; /* in/out */
+ __u64 bytes_used; /* out */
+ __u64 total_bytes; /* out */
+ __u64 unused[379]; /* pad to 4k */
+ __u8 path[BTRFS_DEVICE_PATH_NAME_MAX]; /* out */
+};
+
+struct btrfs_ioctl_fs_info_args {
+ __u64 max_id; /* out */
+ __u64 num_devices; /* out */
+ __u8 fsid[BTRFS_FSID_SIZE]; /* out */
+ __u32 nodesize; /* out */
+ __u32 sectorsize; /* out */
+ __u32 clone_alignment; /* out */
+ __u32 reserved32;
+ __u64 reserved[122]; /* pad to 1k */
+};
+
+/*
+ * feature flags
+ *
+ * Used by:
+ * struct btrfs_ioctl_feature_flags
+ */
+#define BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE (1ULL << 0)
+/*
+ * Older kernels (< 4.9) on big-endian systems produced broken free space tree
+ * bitmaps, and btrfs-progs also used to corrupt the free space tree (versions
+ * < 4.7.3). If this bit is clear, then the free space tree cannot be trusted.
+ * btrfs-progs can also intentionally clear this bit to ask the kernel to
+ * rebuild the free space tree, however this might not work on older kernels
+ * that do not know about this bit. If not sure, clear the cache manually on
+ * first mount when booting older kernel versions.
+ */
+#define BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE_VALID (1ULL << 1)
+
+#define BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF (1ULL << 0)
+#define BTRFS_FEATURE_INCOMPAT_DEFAULT_SUBVOL (1ULL << 1)
+#define BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS (1ULL << 2)
+#define BTRFS_FEATURE_INCOMPAT_COMPRESS_LZO (1ULL << 3)
+#define BTRFS_FEATURE_INCOMPAT_COMPRESS_ZSTD (1ULL << 4)
+
+/*
+ * older kernels tried to do bigger metadata blocks, but the
+ * code was pretty buggy. Lets not let them try anymore.
+ */
+#define BTRFS_FEATURE_INCOMPAT_BIG_METADATA (1ULL << 5)
+
+#define BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF (1ULL << 6)
+#define BTRFS_FEATURE_INCOMPAT_RAID56 (1ULL << 7)
+#define BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA (1ULL << 8)
+#define BTRFS_FEATURE_INCOMPAT_NO_HOLES (1ULL << 9)
+
+struct btrfs_ioctl_feature_flags {
+ __u64 compat_flags;
+ __u64 compat_ro_flags;
+ __u64 incompat_flags;
+};
+
+/* balance control ioctl modes */
+#define BTRFS_BALANCE_CTL_PAUSE 1
+#define BTRFS_BALANCE_CTL_CANCEL 2
+
+/*
+ * this is packed, because it should be exactly the same as its disk
+ * byte order counterpart (struct btrfs_disk_balance_args)
+ */
+struct btrfs_balance_args {
+ __u64 profiles;
+ union {
+ __u64 usage;
+ struct {
+ __u32 usage_min;
+ __u32 usage_max;
+ };
+ };
+ __u64 devid;
+ __u64 pstart;
+ __u64 pend;
+ __u64 vstart;
+ __u64 vend;
+
+ __u64 target;
+
+ __u64 flags;
+
+ /*
+ * BTRFS_BALANCE_ARGS_LIMIT with value 'limit'
+ * BTRFS_BALANCE_ARGS_LIMIT_RANGE - the extend version can use minimum
+ * and maximum
+ */
+ union {
+ __u64 limit; /* limit number of processed chunks */
+ struct {
+ __u32 limit_min;
+ __u32 limit_max;
+ };
+ };
+
+ /*
+ * Process chunks that cross stripes_min..stripes_max devices,
+ * BTRFS_BALANCE_ARGS_STRIPES_RANGE
+ */
+ __u32 stripes_min;
+ __u32 stripes_max;
+
+ __u64 unused[6];
+} __attribute__ ((__packed__));
+
+/* report balance progress to userspace */
+struct btrfs_balance_progress {
+ __u64 expected; /* estimated # of chunks that will be
+ * relocated to fulfill the request */
+ __u64 considered; /* # of chunks we have considered so far */
+ __u64 completed; /* # of chunks relocated so far */
+};
+
+/*
+ * flags definition for balance
+ *
+ * Restriper's general type filter
+ *
+ * Used by:
+ * btrfs_ioctl_balance_args.flags
+ * btrfs_balance_control.flags (internal)
+ */
+#define BTRFS_BALANCE_DATA (1ULL << 0)
+#define BTRFS_BALANCE_SYSTEM (1ULL << 1)
+#define BTRFS_BALANCE_METADATA (1ULL << 2)
+
+#define BTRFS_BALANCE_TYPE_MASK (BTRFS_BALANCE_DATA | \
+ BTRFS_BALANCE_SYSTEM | \
+ BTRFS_BALANCE_METADATA)
+
+#define BTRFS_BALANCE_FORCE (1ULL << 3)
+#define BTRFS_BALANCE_RESUME (1ULL << 4)
+
+/*
+ * flags definitions for per-type balance args
+ *
+ * Balance filters
+ *
+ * Used by:
+ * struct btrfs_balance_args
+ */
+#define BTRFS_BALANCE_ARGS_PROFILES (1ULL << 0)
+#define BTRFS_BALANCE_ARGS_USAGE (1ULL << 1)
+#define BTRFS_BALANCE_ARGS_DEVID (1ULL << 2)
+#define BTRFS_BALANCE_ARGS_DRANGE (1ULL << 3)
+#define BTRFS_BALANCE_ARGS_VRANGE (1ULL << 4)
+#define BTRFS_BALANCE_ARGS_LIMIT (1ULL << 5)
+#define BTRFS_BALANCE_ARGS_LIMIT_RANGE (1ULL << 6)
+#define BTRFS_BALANCE_ARGS_STRIPES_RANGE (1ULL << 7)
+#define BTRFS_BALANCE_ARGS_USAGE_RANGE (1ULL << 10)
+
+#define BTRFS_BALANCE_ARGS_MASK \
+ (BTRFS_BALANCE_ARGS_PROFILES | \
+ BTRFS_BALANCE_ARGS_USAGE | \
+ BTRFS_BALANCE_ARGS_DEVID | \
+ BTRFS_BALANCE_ARGS_DRANGE | \
+ BTRFS_BALANCE_ARGS_VRANGE | \
+ BTRFS_BALANCE_ARGS_LIMIT | \
+ BTRFS_BALANCE_ARGS_LIMIT_RANGE | \
+ BTRFS_BALANCE_ARGS_STRIPES_RANGE | \
+ BTRFS_BALANCE_ARGS_USAGE_RANGE)
+
+/*
+ * Profile changing flags. When SOFT is set we won't relocate chunk if
+ * it already has the target profile (even though it may be
+ * half-filled).
+ */
+#define BTRFS_BALANCE_ARGS_CONVERT (1ULL << 8)
+#define BTRFS_BALANCE_ARGS_SOFT (1ULL << 9)
+
+
+/*
+ * flags definition for balance state
+ *
+ * Used by:
+ * struct btrfs_ioctl_balance_args.state
+ */
+#define BTRFS_BALANCE_STATE_RUNNING (1ULL << 0)
+#define BTRFS_BALANCE_STATE_PAUSE_REQ (1ULL << 1)
+#define BTRFS_BALANCE_STATE_CANCEL_REQ (1ULL << 2)
+
+struct btrfs_ioctl_balance_args {
+ __u64 flags; /* in/out */
+ __u64 state; /* out */
+
+ struct btrfs_balance_args data; /* in/out */
+ struct btrfs_balance_args meta; /* in/out */
+ struct btrfs_balance_args sys; /* in/out */
+
+ struct btrfs_balance_progress stat; /* out */
+
+ __u64 unused[72]; /* pad to 1k */
+};
+
+#define BTRFS_INO_LOOKUP_PATH_MAX 4080
+struct btrfs_ioctl_ino_lookup_args {
+ __u64 treeid;
+ __u64 objectid;
+ char name[BTRFS_INO_LOOKUP_PATH_MAX];
+};
+
+/* Search criteria for the btrfs SEARCH ioctl family. */
+struct btrfs_ioctl_search_key {
+ /*
+ * The tree we're searching in. 1 is the tree of tree roots, 2 is the
+ * extent tree, etc...
+ *
+ * A special tree_id value of 0 will cause a search in the subvolume
+ * tree that the inode which is passed to the ioctl is part of.
+ */
+ __u64 tree_id; /* in */
+
+ /*
+ * When doing a tree search, we're actually taking a slice from a
+ * linear search space of 136-bit keys.
+ *
+ * A full 136-bit tree key is composed as:
+ * (objectid << 72) + (type << 64) + offset
+ *
+ * The individual min and max values for objectid, type and offset
+ * define the min_key and max_key values for the search range. All
+ * metadata items with a key in the interval [min_key, max_key] will be
+ * returned.
+ *
+ * Additionally, we can filter the items returned on transaction id of
+ * the metadata block they're stored in by specifying a transid range.
+ * Be aware that this transaction id only denotes when the metadata
+ * page that currently contains the item got written the last time as
+ * result of a COW operation. The number does not have any meaning
+ * related to the transaction in which an individual item that is being
+ * returned was created or changed.
+ */
+ __u64 min_objectid; /* in */
+ __u64 max_objectid; /* in */
+ __u64 min_offset; /* in */
+ __u64 max_offset; /* in */
+ __u64 min_transid; /* in */
+ __u64 max_transid; /* in */
+ __u32 min_type; /* in */
+ __u32 max_type; /* in */
+
+ /*
+ * input: The maximum amount of results desired.
+ * output: The actual amount of items returned, restricted by any of:
+ * - reaching the upper bound of the search range
+ * - reaching the input nr_items amount of items
+ * - completely filling the supplied memory buffer
+ */
+ __u32 nr_items; /* in/out */
+
+ /* align to 64 bits */
+ __u32 unused;
+
+ /* some extra for later */
+ __u64 unused1;
+ __u64 unused2;
+ __u64 unused3;
+ __u64 unused4;
+};
+
+struct btrfs_ioctl_search_header {
+ __u64 transid;
+ __u64 objectid;
+ __u64 offset;
+ __u32 type;
+ __u32 len;
+};
+
+#define BTRFS_SEARCH_ARGS_BUFSIZE (4096 - sizeof(struct btrfs_ioctl_search_key))
+/*
+ * the buf is an array of search headers where
+ * each header is followed by the actual item
+ * the type field is expanded to 32 bits for alignment
+ */
+struct btrfs_ioctl_search_args {
+ struct btrfs_ioctl_search_key key;
+ char buf[BTRFS_SEARCH_ARGS_BUFSIZE];
+};
+
+struct btrfs_ioctl_search_args_v2 {
+ struct btrfs_ioctl_search_key key; /* in/out - search parameters */
+ __u64 buf_size; /* in - size of buffer
+ * out - on EOVERFLOW: needed size
+ * to store item */
+ __u64 buf[0]; /* out - found items */
+};
+
+struct btrfs_ioctl_clone_range_args {
+ __s64 src_fd;
+ __u64 src_offset, src_length;
+ __u64 dest_offset;
+};
+
+/*
+ * flags definition for the defrag range ioctl
+ *
+ * Used by:
+ * struct btrfs_ioctl_defrag_range_args.flags
+ */
+#define BTRFS_DEFRAG_RANGE_COMPRESS 1
+#define BTRFS_DEFRAG_RANGE_START_IO 2
+struct btrfs_ioctl_defrag_range_args {
+ /* start of the defrag operation */
+ __u64 start;
+
+ /* number of bytes to defrag, use (u64)-1 to say all */
+ __u64 len;
+
+ /*
+ * flags for the operation, which can include turning
+ * on compression for this one defrag
+ */
+ __u64 flags;
+
+ /*
+ * any extent bigger than this will be considered
+ * already defragged. Use 0 to take the kernel default
+ * Use 1 to say every single extent must be rewritten
+ */
+ __u32 extent_thresh;
+
+ /*
+ * which compression method to use if turning on compression
+ * for this defrag operation. If unspecified, zlib will
+ * be used
+ */
+ __u32 compress_type;
+
+ /* spare for later */
+ __u32 unused[4];
+};
+
+
+#define BTRFS_SAME_DATA_DIFFERS 1
+/* For extent-same ioctl */
+struct btrfs_ioctl_same_extent_info {
+ __s64 fd; /* in - destination file */
+ __u64 logical_offset; /* in - start of extent in destination */
+ __u64 bytes_deduped; /* out - total # of bytes we were able
+ * to dedupe from this file */
+ /* status of this dedupe operation:
+ * 0 if dedup succeeds
+ * < 0 for error
+ * == BTRFS_SAME_DATA_DIFFERS if data differs
+ */
+ __s32 status; /* out - see above description */
+ __u32 reserved;
+};
+
+struct btrfs_ioctl_same_args {
+ __u64 logical_offset; /* in - start of extent in source */
+ __u64 length; /* in - length of extent */
+ __u16 dest_count; /* in - total elements in info array */
+ __u16 reserved1;
+ __u32 reserved2;
+ struct btrfs_ioctl_same_extent_info info[0];
+};
+
+struct btrfs_ioctl_space_info {
+ __u64 flags;
+ __u64 total_bytes;
+ __u64 used_bytes;
+};
+
+struct btrfs_ioctl_space_args {
+ __u64 space_slots;
+ __u64 total_spaces;
+ struct btrfs_ioctl_space_info spaces[0];
+};
+
+struct btrfs_data_container {
+ __u32 bytes_left; /* out -- bytes not needed to deliver output */
+ __u32 bytes_missing; /* out -- additional bytes needed for result */
+ __u32 elem_cnt; /* out */
+ __u32 elem_missed; /* out */
+ __u64 val[0]; /* out */
+};
+
+struct btrfs_ioctl_ino_path_args {
+ __u64 inum; /* in */
+ __u64 size; /* in */
+ __u64 reserved[4];
+ /* struct btrfs_data_container *fspath; out */
+ __u64 fspath; /* out */
+};
+
+struct btrfs_ioctl_logical_ino_args {
+ __u64 logical; /* in */
+ __u64 size; /* in */
+ __u64 reserved[4];
+ /* struct btrfs_data_container *inodes; out */
+ __u64 inodes;
+};
+
+enum btrfs_dev_stat_values {
+ /* disk I/O failure stats */
+ BTRFS_DEV_STAT_WRITE_ERRS, /* EIO or EREMOTEIO from lower layers */
+ BTRFS_DEV_STAT_READ_ERRS, /* EIO or EREMOTEIO from lower layers */
+ BTRFS_DEV_STAT_FLUSH_ERRS, /* EIO or EREMOTEIO from lower layers */
+
+ /* stats for indirect indications for I/O failures */
+ BTRFS_DEV_STAT_CORRUPTION_ERRS, /* checksum error, bytenr error or
+ * contents is illegal: this is an
+ * indication that the block was damaged
+ * during read or write, or written to
+ * wrong location or read from wrong
+ * location */
+ BTRFS_DEV_STAT_GENERATION_ERRS, /* an indication that blocks have not
+ * been written */
+
+ BTRFS_DEV_STAT_VALUES_MAX
+};
+
+/* Reset statistics after reading; needs SYS_ADMIN capability */
+#define BTRFS_DEV_STATS_RESET (1ULL << 0)
+
+struct btrfs_ioctl_get_dev_stats {
+ __u64 devid; /* in */
+ __u64 nr_items; /* in/out */
+ __u64 flags; /* in/out */
+
+ /* out values: */
+ __u64 values[BTRFS_DEV_STAT_VALUES_MAX];
+
+ __u64 unused[128 - 2 - BTRFS_DEV_STAT_VALUES_MAX]; /* pad to 1k */
+};
+
+#define BTRFS_QUOTA_CTL_ENABLE 1
+#define BTRFS_QUOTA_CTL_DISABLE 2
+#define BTRFS_QUOTA_CTL_RESCAN__NOTUSED 3
+struct btrfs_ioctl_quota_ctl_args {
+ __u64 cmd;
+ __u64 status;
+};
+
+struct btrfs_ioctl_quota_rescan_args {
+ __u64 flags;
+ __u64 progress;
+ __u64 reserved[6];
+};
+
+struct btrfs_ioctl_qgroup_assign_args {
+ __u64 assign;
+ __u64 src;
+ __u64 dst;
+};
+
+struct btrfs_ioctl_qgroup_create_args {
+ __u64 create;
+ __u64 qgroupid;
+};
+struct btrfs_ioctl_timespec {
+ __u64 sec;
+ __u32 nsec;
+};
+
+struct btrfs_ioctl_received_subvol_args {
+ char uuid[BTRFS_UUID_SIZE]; /* in */
+ __u64 stransid; /* in */
+ __u64 rtransid; /* out */
+ struct btrfs_ioctl_timespec stime; /* in */
+ struct btrfs_ioctl_timespec rtime; /* out */
+ __u64 flags; /* in */
+ __u64 reserved[16]; /* in */
+};
+
+/*
+ * Caller doesn't want file data in the send stream, even if the
+ * search of clone sources doesn't find an extent. UPDATE_EXTENT
+ * commands will be sent instead of WRITE commands.
+ */
+#define BTRFS_SEND_FLAG_NO_FILE_DATA 0x1
+
+/*
+ * Do not add the leading stream header. Used when multiple snapshots
+ * are sent back to back.
+ */
+#define BTRFS_SEND_FLAG_OMIT_STREAM_HEADER 0x2
+
+/*
+ * Omit the command at the end of the stream that indicated the end
+ * of the stream. This option is used when multiple snapshots are
+ * sent back to back.
+ */
+#define BTRFS_SEND_FLAG_OMIT_END_CMD 0x4
+
+#define BTRFS_SEND_FLAG_MASK \
+ (BTRFS_SEND_FLAG_NO_FILE_DATA | \
+ BTRFS_SEND_FLAG_OMIT_STREAM_HEADER | \
+ BTRFS_SEND_FLAG_OMIT_END_CMD)
+
+struct btrfs_ioctl_send_args {
+ __s64 send_fd; /* in */
+ __u64 clone_sources_count; /* in */
+ __u64 *clone_sources; /* in */
+ __u64 parent_root; /* in */
+ __u64 flags; /* in */
+ __u64 reserved[4]; /* in */
+};
+
+/* Error codes as returned by the kernel */
+enum btrfs_err_code {
+ BTRFS_ERROR_DEV_RAID1_MIN_NOT_MET = 1,
+ BTRFS_ERROR_DEV_RAID10_MIN_NOT_MET,
+ BTRFS_ERROR_DEV_RAID5_MIN_NOT_MET,
+ BTRFS_ERROR_DEV_RAID6_MIN_NOT_MET,
+ BTRFS_ERROR_DEV_TGT_REPLACE,
+ BTRFS_ERROR_DEV_MISSING_NOT_FOUND,
+ BTRFS_ERROR_DEV_ONLY_WRITABLE,
+ BTRFS_ERROR_DEV_EXCL_RUN_IN_PROGRESS
+};
+
+#define BTRFS_IOC_SNAP_CREATE _IOW(BTRFS_IOCTL_MAGIC, 1, \
+ struct btrfs_ioctl_vol_args)
+#define BTRFS_IOC_DEFRAG _IOW(BTRFS_IOCTL_MAGIC, 2, \
+ struct btrfs_ioctl_vol_args)
+#define BTRFS_IOC_RESIZE _IOW(BTRFS_IOCTL_MAGIC, 3, \
+ struct btrfs_ioctl_vol_args)
+#define BTRFS_IOC_SCAN_DEV _IOW(BTRFS_IOCTL_MAGIC, 4, \
+ struct btrfs_ioctl_vol_args)
+/* trans start and trans end are dangerous, and only for
+ * use by applications that know how to avoid the
+ * resulting deadlocks
+ */
+#define BTRFS_IOC_TRANS_START _IO(BTRFS_IOCTL_MAGIC, 6)
+#define BTRFS_IOC_TRANS_END _IO(BTRFS_IOCTL_MAGIC, 7)
+#define BTRFS_IOC_SYNC _IO(BTRFS_IOCTL_MAGIC, 8)
+
+#define BTRFS_IOC_CLONE _IOW(BTRFS_IOCTL_MAGIC, 9, int)
+#define BTRFS_IOC_ADD_DEV _IOW(BTRFS_IOCTL_MAGIC, 10, \
+ struct btrfs_ioctl_vol_args)
+#define BTRFS_IOC_RM_DEV _IOW(BTRFS_IOCTL_MAGIC, 11, \
+ struct btrfs_ioctl_vol_args)
+#define BTRFS_IOC_BALANCE _IOW(BTRFS_IOCTL_MAGIC, 12, \
+ struct btrfs_ioctl_vol_args)
+
+#define BTRFS_IOC_CLONE_RANGE _IOW(BTRFS_IOCTL_MAGIC, 13, \
+ struct btrfs_ioctl_clone_range_args)
+
+#define BTRFS_IOC_SUBVOL_CREATE _IOW(BTRFS_IOCTL_MAGIC, 14, \
+ struct btrfs_ioctl_vol_args)
+#define BTRFS_IOC_SNAP_DESTROY _IOW(BTRFS_IOCTL_MAGIC, 15, \
+ struct btrfs_ioctl_vol_args)
+#define BTRFS_IOC_DEFRAG_RANGE _IOW(BTRFS_IOCTL_MAGIC, 16, \
+ struct btrfs_ioctl_defrag_range_args)
+#define BTRFS_IOC_TREE_SEARCH _IOWR(BTRFS_IOCTL_MAGIC, 17, \
+ struct btrfs_ioctl_search_args)
+#define BTRFS_IOC_TREE_SEARCH_V2 _IOWR(BTRFS_IOCTL_MAGIC, 17, \
+ struct btrfs_ioctl_search_args_v2)
+#define BTRFS_IOC_INO_LOOKUP _IOWR(BTRFS_IOCTL_MAGIC, 18, \
+ struct btrfs_ioctl_ino_lookup_args)
+#define BTRFS_IOC_DEFAULT_SUBVOL _IOW(BTRFS_IOCTL_MAGIC, 19, __u64)
+#define BTRFS_IOC_SPACE_INFO _IOWR(BTRFS_IOCTL_MAGIC, 20, \
+ struct btrfs_ioctl_space_args)
+#define BTRFS_IOC_START_SYNC _IOR(BTRFS_IOCTL_MAGIC, 24, __u64)
+#define BTRFS_IOC_WAIT_SYNC _IOW(BTRFS_IOCTL_MAGIC, 22, __u64)
+#define BTRFS_IOC_SNAP_CREATE_V2 _IOW(BTRFS_IOCTL_MAGIC, 23, \
+ struct btrfs_ioctl_vol_args_v2)
+#define BTRFS_IOC_SUBVOL_CREATE_V2 _IOW(BTRFS_IOCTL_MAGIC, 24, \
+ struct btrfs_ioctl_vol_args_v2)
+#define BTRFS_IOC_SUBVOL_GETFLAGS _IOR(BTRFS_IOCTL_MAGIC, 25, __u64)
+#define BTRFS_IOC_SUBVOL_SETFLAGS _IOW(BTRFS_IOCTL_MAGIC, 26, __u64)
+#define BTRFS_IOC_SCRUB _IOWR(BTRFS_IOCTL_MAGIC, 27, \
+ struct btrfs_ioctl_scrub_args)
+#define BTRFS_IOC_SCRUB_CANCEL _IO(BTRFS_IOCTL_MAGIC, 28)
+#define BTRFS_IOC_SCRUB_PROGRESS _IOWR(BTRFS_IOCTL_MAGIC, 29, \
+ struct btrfs_ioctl_scrub_args)
+#define BTRFS_IOC_DEV_INFO _IOWR(BTRFS_IOCTL_MAGIC, 30, \
+ struct btrfs_ioctl_dev_info_args)
+#define BTRFS_IOC_FS_INFO _IOR(BTRFS_IOCTL_MAGIC, 31, \
+ struct btrfs_ioctl_fs_info_args)
+#define BTRFS_IOC_BALANCE_V2 _IOWR(BTRFS_IOCTL_MAGIC, 32, \
+ struct btrfs_ioctl_balance_args)
+#define BTRFS_IOC_BALANCE_CTL _IOW(BTRFS_IOCTL_MAGIC, 33, int)
+#define BTRFS_IOC_BALANCE_PROGRESS _IOR(BTRFS_IOCTL_MAGIC, 34, \
+ struct btrfs_ioctl_balance_args)
+#define BTRFS_IOC_INO_PATHS _IOWR(BTRFS_IOCTL_MAGIC, 35, \
+ struct btrfs_ioctl_ino_path_args)
+#define BTRFS_IOC_LOGICAL_INO _IOWR(BTRFS_IOCTL_MAGIC, 36, \
+ struct btrfs_ioctl_logical_ino_args)
+#define BTRFS_IOC_SET_RECEIVED_SUBVOL _IOWR(BTRFS_IOCTL_MAGIC, 37, \
+ struct btrfs_ioctl_received_subvol_args)
+#define BTRFS_IOC_SEND _IOW(BTRFS_IOCTL_MAGIC, 38, struct btrfs_ioctl_send_args)
+#define BTRFS_IOC_DEVICES_READY _IOR(BTRFS_IOCTL_MAGIC, 39, \
+ struct btrfs_ioctl_vol_args)
+#define BTRFS_IOC_QUOTA_CTL _IOWR(BTRFS_IOCTL_MAGIC, 40, \
+ struct btrfs_ioctl_quota_ctl_args)
+#define BTRFS_IOC_QGROUP_ASSIGN _IOW(BTRFS_IOCTL_MAGIC, 41, \
+ struct btrfs_ioctl_qgroup_assign_args)
+#define BTRFS_IOC_QGROUP_CREATE _IOW(BTRFS_IOCTL_MAGIC, 42, \
+ struct btrfs_ioctl_qgroup_create_args)
+#define BTRFS_IOC_QGROUP_LIMIT _IOR(BTRFS_IOCTL_MAGIC, 43, \
+ struct btrfs_ioctl_qgroup_limit_args)
+#define BTRFS_IOC_QUOTA_RESCAN _IOW(BTRFS_IOCTL_MAGIC, 44, \
+ struct btrfs_ioctl_quota_rescan_args)
+#define BTRFS_IOC_QUOTA_RESCAN_STATUS _IOR(BTRFS_IOCTL_MAGIC, 45, \
+ struct btrfs_ioctl_quota_rescan_args)
+#define BTRFS_IOC_QUOTA_RESCAN_WAIT _IO(BTRFS_IOCTL_MAGIC, 46)
+#define BTRFS_IOC_GET_FSLABEL _IOR(BTRFS_IOCTL_MAGIC, 49, \
+ char[BTRFS_LABEL_SIZE])
+#define BTRFS_IOC_SET_FSLABEL _IOW(BTRFS_IOCTL_MAGIC, 50, \
+ char[BTRFS_LABEL_SIZE])
+#define BTRFS_IOC_GET_DEV_STATS _IOWR(BTRFS_IOCTL_MAGIC, 52, \
+ struct btrfs_ioctl_get_dev_stats)
+#define BTRFS_IOC_DEV_REPLACE _IOWR(BTRFS_IOCTL_MAGIC, 53, \
+ struct btrfs_ioctl_dev_replace_args)
+#define BTRFS_IOC_FILE_EXTENT_SAME _IOWR(BTRFS_IOCTL_MAGIC, 54, \
+ struct btrfs_ioctl_same_args)
+#define BTRFS_IOC_GET_FEATURES _IOR(BTRFS_IOCTL_MAGIC, 57, \
+ struct btrfs_ioctl_feature_flags)
+#define BTRFS_IOC_SET_FEATURES _IOW(BTRFS_IOCTL_MAGIC, 57, \
+ struct btrfs_ioctl_feature_flags[2])
+#define BTRFS_IOC_GET_SUPPORTED_FEATURES _IOR(BTRFS_IOCTL_MAGIC, 57, \
+ struct btrfs_ioctl_feature_flags[3])
+#define BTRFS_IOC_RM_DEV_V2 _IOW(BTRFS_IOCTL_MAGIC, 58, \
+ struct btrfs_ioctl_vol_args_v2)
+
+#endif /* _LINUX_BTRFS_H */
diff --git a/libbtrfsutil/btrfs_tree.h b/libbtrfsutil/btrfs_tree.h
new file mode 100644
index 00000000..f2ac0267
--- /dev/null
+++ b/libbtrfsutil/btrfs_tree.h
@@ -0,0 +1,970 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef _BTRFS_CTREE_H_
+#define _BTRFS_CTREE_H_
+
+#include "btrfs.h"
+#include <linux/types.h>
+
+/*
+ * This header contains the structure definitions and constants used
+ * by file system objects that can be retrieved using
+ * the BTRFS_IOC_SEARCH_TREE ioctl. That means basically anything that
+ * is needed to describe a leaf node's key or item contents.
+ */
+
+/* holds pointers to all of the tree roots */
+#define BTRFS_ROOT_TREE_OBJECTID 1ULL
+
+/* stores information about which extents are in use, and reference counts */
+#define BTRFS_EXTENT_TREE_OBJECTID 2ULL
+
+/*
+ * chunk tree stores translations from logical -> physical block numbering
+ * the super block points to the chunk tree
+ */
+#define BTRFS_CHUNK_TREE_OBJECTID 3ULL
+
+/*
+ * stores information about which areas of a given device are in use.
+ * one per device. The tree of tree roots points to the device tree
+ */
+#define BTRFS_DEV_TREE_OBJECTID 4ULL
+
+/* one per subvolume, storing files and directories */
+#define BTRFS_FS_TREE_OBJECTID 5ULL
+
+/* directory objectid inside the root tree */
+#define BTRFS_ROOT_TREE_DIR_OBJECTID 6ULL
+
+/* holds checksums of all the data extents */
+#define BTRFS_CSUM_TREE_OBJECTID 7ULL
+
+/* holds quota configuration and tracking */
+#define BTRFS_QUOTA_TREE_OBJECTID 8ULL
+
+/* for storing items that use the BTRFS_UUID_KEY* types */
+#define BTRFS_UUID_TREE_OBJECTID 9ULL
+
+/* tracks free space in block groups. */
+#define BTRFS_FREE_SPACE_TREE_OBJECTID 10ULL
+
+/* device stats in the device tree */
+#define BTRFS_DEV_STATS_OBJECTID 0ULL
+
+/* for storing balance parameters in the root tree */
+#define BTRFS_BALANCE_OBJECTID -4ULL
+
+/* orhpan objectid for tracking unlinked/truncated files */
+#define BTRFS_ORPHAN_OBJECTID -5ULL
+
+/* does write ahead logging to speed up fsyncs */
+#define BTRFS_TREE_LOG_OBJECTID -6ULL
+#define BTRFS_TREE_LOG_FIXUP_OBJECTID -7ULL
+
+/* for space balancing */
+#define BTRFS_TREE_RELOC_OBJECTID -8ULL
+#define BTRFS_DATA_RELOC_TREE_OBJECTID -9ULL
+
+/*
+ * extent checksums all have this objectid
+ * this allows them to share the logging tree
+ * for fsyncs
+ */
+#define BTRFS_EXTENT_CSUM_OBJECTID -10ULL
+
+/* For storing free space cache */
+#define BTRFS_FREE_SPACE_OBJECTID -11ULL
+
+/*
+ * The inode number assigned to the special inode for storing
+ * free ino cache
+ */
+#define BTRFS_FREE_INO_OBJECTID -12ULL
+
+/* dummy objectid represents multiple objectids */
+#define BTRFS_MULTIPLE_OBJECTIDS -255ULL
+
+/*
+ * All files have objectids in this range.
+ */
+#define BTRFS_FIRST_FREE_OBJECTID 256ULL
+#define BTRFS_LAST_FREE_OBJECTID -256ULL
+#define BTRFS_FIRST_CHUNK_TREE_OBJECTID 256ULL
+
+
+/*
+ * the device items go into the chunk tree. The key is in the form
+ * [ 1 BTRFS_DEV_ITEM_KEY device_id ]
+ */
+#define BTRFS_DEV_ITEMS_OBJECTID 1ULL
+
+#define BTRFS_BTREE_INODE_OBJECTID 1
+
+#define BTRFS_EMPTY_SUBVOL_DIR_OBJECTID 2
+
+#define BTRFS_DEV_REPLACE_DEVID 0ULL
+
+/*
+ * inode items have the data typically returned from stat and store other
+ * info about object characteristics. There is one for every file and dir in
+ * the FS
+ */
+#define BTRFS_INODE_ITEM_KEY 1
+#define BTRFS_INODE_REF_KEY 12
+#define BTRFS_INODE_EXTREF_KEY 13
+#define BTRFS_XATTR_ITEM_KEY 24
+#define BTRFS_ORPHAN_ITEM_KEY 48
+/* reserve 2-15 close to the inode for later flexibility */
+
+/*
+ * dir items are the name -> inode pointers in a directory. There is one
+ * for every name in a directory.
+ */
+#define BTRFS_DIR_LOG_ITEM_KEY 60
+#define BTRFS_DIR_LOG_INDEX_KEY 72
+#define BTRFS_DIR_ITEM_KEY 84
+#define BTRFS_DIR_INDEX_KEY 96
+/*
+ * extent data is for file data
+ */
+#define BTRFS_EXTENT_DATA_KEY 108
+
+/*
+ * extent csums are stored in a separate tree and hold csums for
+ * an entire extent on disk.
+ */
+#define BTRFS_EXTENT_CSUM_KEY 128
+
+/*
+ * root items point to tree roots. They are typically in the root
+ * tree used by the super block to find all the other trees
+ */
+#define BTRFS_ROOT_ITEM_KEY 132
+
+/*
+ * root backrefs tie subvols and snapshots to the directory entries that
+ * reference them
+ */
+#define BTRFS_ROOT_BACKREF_KEY 144
+
+/*
+ * root refs make a fast index for listing all of the snapshots and
+ * subvolumes referenced by a given root. They point directly to the
+ * directory item in the root that references the subvol
+ */
+#define BTRFS_ROOT_REF_KEY 156
+
+/*
+ * extent items are in the extent map tree. These record which blocks
+ * are used, and how many references there are to each block
+ */
+#define BTRFS_EXTENT_ITEM_KEY 168
+
+/*
+ * The same as the BTRFS_EXTENT_ITEM_KEY, except it's metadata we already know
+ * the length, so we save the level in key->offset instead of the length.
+ */
+#define BTRFS_METADATA_ITEM_KEY 169
+
+#define BTRFS_TREE_BLOCK_REF_KEY 176
+
+#define BTRFS_EXTENT_DATA_REF_KEY 178
+
+#define BTRFS_EXTENT_REF_V0_KEY 180
+
+#define BTRFS_SHARED_BLOCK_REF_KEY 182
+
+#define BTRFS_SHARED_DATA_REF_KEY 184
+
+/*
+ * block groups give us hints into the extent allocation trees. Which
+ * blocks are free etc etc
+ */
+#define BTRFS_BLOCK_GROUP_ITEM_KEY 192
+
+/*
+ * Every block group is represented in the free space tree by a free space info
+ * item, which stores some accounting information. It is keyed on
+ * (block_group_start, FREE_SPACE_INFO, block_group_length).
+ */
+#define BTRFS_FREE_SPACE_INFO_KEY 198
+
+/*
+ * A free space extent tracks an extent of space that is free in a block group.
+ * It is keyed on (start, FREE_SPACE_EXTENT, length).
+ */
+#define BTRFS_FREE_SPACE_EXTENT_KEY 199
+
+/*
+ * When a block group becomes very fragmented, we convert it to use bitmaps
+ * instead of extents. A free space bitmap is keyed on
+ * (start, FREE_SPACE_BITMAP, length); the corresponding item is a bitmap with
+ * (length / sectorsize) bits.
+ */
+#define BTRFS_FREE_SPACE_BITMAP_KEY 200
+
+#define BTRFS_DEV_EXTENT_KEY 204
+#define BTRFS_DEV_ITEM_KEY 216
+#define BTRFS_CHUNK_ITEM_KEY 228
+
+/*
+ * Records the overall state of the qgroups.
+ * There's only one instance of this key present,
+ * (0, BTRFS_QGROUP_STATUS_KEY, 0)
+ */
+#define BTRFS_QGROUP_STATUS_KEY 240
+/*
+ * Records the currently used space of the qgroup.
+ * One key per qgroup, (0, BTRFS_QGROUP_INFO_KEY, qgroupid).
+ */
+#define BTRFS_QGROUP_INFO_KEY 242
+/*
+ * Contains the user configured limits for the qgroup.
+ * One key per qgroup, (0, BTRFS_QGROUP_LIMIT_KEY, qgroupid).
+ */
+#define BTRFS_QGROUP_LIMIT_KEY 244
+/*
+ * Records the child-parent relationship of qgroups. For
+ * each relation, 2 keys are present:
+ * (childid, BTRFS_QGROUP_RELATION_KEY, parentid)
+ * (parentid, BTRFS_QGROUP_RELATION_KEY, childid)
+ */
+#define BTRFS_QGROUP_RELATION_KEY 246
+
+/*
+ * Obsolete name, see BTRFS_TEMPORARY_ITEM_KEY.
+ */
+#define BTRFS_BALANCE_ITEM_KEY 248
+
+/*
+ * The key type for tree items that are stored persistently, but do not need to
+ * exist for extended period of time. The items can exist in any tree.
+ *
+ * [subtype, BTRFS_TEMPORARY_ITEM_KEY, data]
+ *
+ * Existing items:
+ *
+ * - balance status item
+ * (BTRFS_BALANCE_OBJECTID, BTRFS_TEMPORARY_ITEM_KEY, 0)
+ */
+#define BTRFS_TEMPORARY_ITEM_KEY 248
+
+/*
+ * Obsolete name, see BTRFS_PERSISTENT_ITEM_KEY
+ */
+#define BTRFS_DEV_STATS_KEY 249
+
+/*
+ * The key type for tree items that are stored persistently and usually exist
+ * for a long period, eg. filesystem lifetime. The item kinds can be status
+ * information, stats or preference values. The item can exist in any tree.
+ *
+ * [subtype, BTRFS_PERSISTENT_ITEM_KEY, data]
+ *
+ * Existing items:
+ *
+ * - device statistics, store IO stats in the device tree, one key for all
+ * stats
+ * (BTRFS_DEV_STATS_OBJECTID, BTRFS_DEV_STATS_KEY, 0)
+ */
+#define BTRFS_PERSISTENT_ITEM_KEY 249
+
+/*
+ * Persistantly stores the device replace state in the device tree.
+ * The key is built like this: (0, BTRFS_DEV_REPLACE_KEY, 0).
+ */
+#define BTRFS_DEV_REPLACE_KEY 250
+
+/*
+ * Stores items that allow to quickly map UUIDs to something else.
+ * These items are part of the filesystem UUID tree.
+ * The key is built like this:
+ * (UUID_upper_64_bits, BTRFS_UUID_KEY*, UUID_lower_64_bits).
+ */
+#if BTRFS_UUID_SIZE != 16
+#error "UUID items require BTRFS_UUID_SIZE == 16!"
+#endif
+#define BTRFS_UUID_KEY_SUBVOL 251 /* for UUIDs assigned to subvols */
+#define BTRFS_UUID_KEY_RECEIVED_SUBVOL 252 /* for UUIDs assigned to
+ * received subvols */
+
+/*
+ * string items are for debugging. They just store a short string of
+ * data in the FS
+ */
+#define BTRFS_STRING_ITEM_KEY 253
+
+
+
+/* 32 bytes in various csum fields */
+#define BTRFS_CSUM_SIZE 32
+
+/* csum types */
+#define BTRFS_CSUM_TYPE_CRC32 0
+
+/*
+ * flags definitions for directory entry item type
+ *
+ * Used by:
+ * struct btrfs_dir_item.type
+ */
+#define BTRFS_FT_UNKNOWN 0
+#define BTRFS_FT_REG_FILE 1
+#define BTRFS_FT_DIR 2
+#define BTRFS_FT_CHRDEV 3
+#define BTRFS_FT_BLKDEV 4
+#define BTRFS_FT_FIFO 5
+#define BTRFS_FT_SOCK 6
+#define BTRFS_FT_SYMLINK 7
+#define BTRFS_FT_XATTR 8
+#define BTRFS_FT_MAX 9
+
+/*
+ * The key defines the order in the tree, and so it also defines (optimal)
+ * block layout.
+ *
+ * objectid corresponds to the inode number.
+ *
+ * type tells us things about the object, and is a kind of stream selector.
+ * so for a given inode, keys with type of 1 might refer to the inode data,
+ * type of 2 may point to file data in the btree and type == 3 may point to
+ * extents.
+ *
+ * offset is the starting byte offset for this key in the stream.
+ *
+ * btrfs_disk_key is in disk byte order. struct btrfs_key is always
+ * in cpu native order. Otherwise they are identical and their sizes
+ * should be the same (ie both packed)
+ */
+struct btrfs_disk_key {
+ __le64 objectid;
+ __u8 type;
+ __le64 offset;
+} __attribute__ ((__packed__));
+
+struct btrfs_key {
+ __u64 objectid;
+ __u8 type;
+ __u64 offset;
+} __attribute__ ((__packed__));
+
+struct btrfs_dev_item {
+ /* the internal btrfs device id */
+ __le64 devid;
+
+ /* size of the device */
+ __le64 total_bytes;
+
+ /* bytes used */
+ __le64 bytes_used;
+
+ /* optimal io alignment for this device */
+ __le32 io_align;
+
+ /* optimal io width for this device */
+ __le32 io_width;
+
+ /* minimal io size for this device */
+ __le32 sector_size;
+
+ /* type and info about this device */
+ __le64 type;
+
+ /* expected generation for this device */
+ __le64 generation;
+
+ /*
+ * starting byte of this partition on the device,
+ * to allow for stripe alignment in the future
+ */
+ __le64 start_offset;
+
+ /* grouping information for allocation decisions */
+ __le32 dev_group;
+
+ /* seek speed 0-100 where 100 is fastest */
+ __u8 seek_speed;
+
+ /* bandwidth 0-100 where 100 is fastest */
+ __u8 bandwidth;
+
+ /* btrfs generated uuid for this device */
+ __u8 uuid[BTRFS_UUID_SIZE];
+
+ /* uuid of FS who owns this device */
+ __u8 fsid[BTRFS_UUID_SIZE];
+} __attribute__ ((__packed__));
+
+struct btrfs_stripe {
+ __le64 devid;
+ __le64 offset;
+ __u8 dev_uuid[BTRFS_UUID_SIZE];
+} __attribute__ ((__packed__));
+
+struct btrfs_chunk {
+ /* size of this chunk in bytes */
+ __le64 length;
+
+ /* objectid of the root referencing this chunk */
+ __le64 owner;
+
+ __le64 stripe_len;
+ __le64 type;
+
+ /* optimal io alignment for this chunk */
+ __le32 io_align;
+
+ /* optimal io width for this chunk */
+ __le32 io_width;
+
+ /* minimal io size for this chunk */
+ __le32 sector_size;
+
+ /* 2^16 stripes is quite a lot, a second limit is the size of a single
+ * item in the btree
+ */
+ __le16 num_stripes;
+
+ /* sub stripes only matter for raid10 */
+ __le16 sub_stripes;
+ struct btrfs_stripe stripe;
+ /* additional stripes go here */
+} __attribute__ ((__packed__));
+
+#define BTRFS_FREE_SPACE_EXTENT 1
+#define BTRFS_FREE_SPACE_BITMAP 2
+
+struct btrfs_free_space_entry {
+ __le64 offset;
+ __le64 bytes;
+ __u8 type;
+} __attribute__ ((__packed__));
+
+struct btrfs_free_space_header {
+ struct btrfs_disk_key location;
+ __le64 generation;
+ __le64 num_entries;
+ __le64 num_bitmaps;
+} __attribute__ ((__packed__));
+
+#define BTRFS_HEADER_FLAG_WRITTEN (1ULL << 0)
+#define BTRFS_HEADER_FLAG_RELOC (1ULL << 1)
+
+/* Super block flags */
+/* Errors detected */
+#define BTRFS_SUPER_FLAG_ERROR (1ULL << 2)
+
+#define BTRFS_SUPER_FLAG_SEEDING (1ULL << 32)
+#define BTRFS_SUPER_FLAG_METADUMP (1ULL << 33)
+
+
+/*
+ * items in the extent btree are used to record the objectid of the
+ * owner of the block and the number of references
+ */
+
+struct btrfs_extent_item {
+ __le64 refs;
+ __le64 generation;
+ __le64 flags;
+} __attribute__ ((__packed__));
+
+struct btrfs_extent_item_v0 {
+ __le32 refs;
+} __attribute__ ((__packed__));
+
+
+#define BTRFS_EXTENT_FLAG_DATA (1ULL << 0)
+#define BTRFS_EXTENT_FLAG_TREE_BLOCK (1ULL << 1)
+
+/* following flags only apply to tree blocks */
+
+/* use full backrefs for extent pointers in the block */
+#define BTRFS_BLOCK_FLAG_FULL_BACKREF (1ULL << 8)
+
+/*
+ * this flag is only used internally by scrub and may be changed at any time
+ * it is only declared here to avoid collisions
+ */
+#define BTRFS_EXTENT_FLAG_SUPER (1ULL << 48)
+
+struct btrfs_tree_block_info {
+ struct btrfs_disk_key key;
+ __u8 level;
+} __attribute__ ((__packed__));
+
+struct btrfs_extent_data_ref {
+ __le64 root;
+ __le64 objectid;
+ __le64 offset;
+ __le32 count;
+} __attribute__ ((__packed__));
+
+struct btrfs_shared_data_ref {
+ __le32 count;
+} __attribute__ ((__packed__));
+
+struct btrfs_extent_inline_ref {
+ __u8 type;
+ __le64 offset;
+} __attribute__ ((__packed__));
+
+/* old style backrefs item */
+struct btrfs_extent_ref_v0 {
+ __le64 root;
+ __le64 generation;
+ __le64 objectid;
+ __le32 count;
+} __attribute__ ((__packed__));
+
+
+/* dev extents record free space on individual devices. The owner
+ * field points back to the chunk allocation mapping tree that allocated
+ * the extent. The chunk tree uuid field is a way to double check the owner
+ */
+struct btrfs_dev_extent {
+ __le64 chunk_tree;
+ __le64 chunk_objectid;
+ __le64 chunk_offset;
+ __le64 length;
+ __u8 chunk_tree_uuid[BTRFS_UUID_SIZE];
+} __attribute__ ((__packed__));
+
+struct btrfs_inode_ref {
+ __le64 index;
+ __le16 name_len;
+ /* name goes here */
+} __attribute__ ((__packed__));
+
+struct btrfs_inode_extref {
+ __le64 parent_objectid;
+ __le64 index;
+ __le16 name_len;
+ __u8 name[0];
+ /* name goes here */
+} __attribute__ ((__packed__));
+
+struct btrfs_timespec {
+ __le64 sec;
+ __le32 nsec;
+} __attribute__ ((__packed__));
+
+struct btrfs_inode_item {
+ /* nfs style generation number */
+ __le64 generation;
+ /* transid that last touched this inode */
+ __le64 transid;
+ __le64 size;
+ __le64 nbytes;
+ __le64 block_group;
+ __le32 nlink;
+ __le32 uid;
+ __le32 gid;
+ __le32 mode;
+ __le64 rdev;
+ __le64 flags;
+
+ /* modification sequence number for NFS */
+ __le64 sequence;
+
+ /*
+ * a little future expansion, for more than this we can
+ * just grow the inode item and version it
+ */
+ __le64 reserved[4];
+ struct btrfs_timespec atime;
+ struct btrfs_timespec ctime;
+ struct btrfs_timespec mtime;
+ struct btrfs_timespec otime;
+} __attribute__ ((__packed__));
+
+struct btrfs_dir_log_item {
+ __le64 end;
+} __attribute__ ((__packed__));
+
+struct btrfs_dir_item {
+ struct btrfs_disk_key location;
+ __le64 transid;
+ __le16 data_len;
+ __le16 name_len;
+ __u8 type;
+} __attribute__ ((__packed__));
+
+#define BTRFS_ROOT_SUBVOL_RDONLY (1ULL << 0)
+
+/*
+ * Internal in-memory flag that a subvolume has been marked for deletion but
+ * still visible as a directory
+ */
+#define BTRFS_ROOT_SUBVOL_DEAD (1ULL << 48)
+
+struct btrfs_root_item {
+ struct btrfs_inode_item inode;
+ __le64 generation;
+ __le64 root_dirid;
+ __le64 bytenr;
+ __le64 byte_limit;
+ __le64 bytes_used;
+ __le64 last_snapshot;
+ __le64 flags;
+ __le32 refs;
+ struct btrfs_disk_key drop_progress;
+ __u8 drop_level;
+ __u8 level;
+
+ /*
+ * The following fields appear after subvol_uuids+subvol_times
+ * were introduced.
+ */
+
+ /*
+ * This generation number is used to test if the new fields are valid
+ * 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
+ * new fields. See btrfs_update_root and btrfs_find_last_root for
+ * details.
+ * the offset of generation_v2 is also used as the start for the memset
+ * when invalidating the fields.
+ */
+ __le64 generation_v2;
+ __u8 uuid[BTRFS_UUID_SIZE];
+ __u8 parent_uuid[BTRFS_UUID_SIZE];
+ __u8 received_uuid[BTRFS_UUID_SIZE];
+ __le64 ctransid; /* updated when an inode changes */
+ __le64 otransid; /* trans when created */
+ __le64 stransid; /* trans when sent. non-zero for received subvol */
+ __le64 rtransid; /* trans when received. non-zero for received subvol */
+ struct btrfs_timespec ctime;
+ struct btrfs_timespec otime;
+ struct btrfs_timespec stime;
+ struct btrfs_timespec rtime;
+ __le64 reserved[8]; /* for future */
+} __attribute__ ((__packed__));
+
+/*
+ * this is used for both forward and backward root refs
+ */
+struct btrfs_root_ref {
+ __le64 dirid;
+ __le64 sequence;
+ __le16 name_len;
+} __attribute__ ((__packed__));
+
+struct btrfs_disk_balance_args {
+ /*
+ * profiles to operate on, single is denoted by
+ * BTRFS_AVAIL_ALLOC_BIT_SINGLE
+ */
+ __le64 profiles;
+
+ /*
+ * usage filter
+ * BTRFS_BALANCE_ARGS_USAGE with a single value means '0..N'
+ * BTRFS_BALANCE_ARGS_USAGE_RANGE - range syntax, min..max
+ */
+ union {
+ __le64 usage;
+ struct {
+ __le32 usage_min;
+ __le32 usage_max;
+ };
+ };
+
+ /* devid filter */
+ __le64 devid;
+
+ /* devid subset filter [pstart..pend) */
+ __le64 pstart;
+ __le64 pend;
+
+ /* btrfs virtual address space subset filter [vstart..vend) */
+ __le64 vstart;
+ __le64 vend;
+
+ /*
+ * profile to convert to, single is denoted by
+ * BTRFS_AVAIL_ALLOC_BIT_SINGLE
+ */
+ __le64 target;
+
+ /* BTRFS_BALANCE_ARGS_* */
+ __le64 flags;
+
+ /*
+ * BTRFS_BALANCE_ARGS_LIMIT with value 'limit'
+ * BTRFS_BALANCE_ARGS_LIMIT_RANGE - the extend version can use minimum
+ * and maximum
+ */
+ union {
+ __le64 limit;
+ struct {
+ __le32 limit_min;
+ __le32 limit_max;
+ };
+ };
+
+ /*
+ * Process chunks that cross stripes_min..stripes_max devices,
+ * BTRFS_BALANCE_ARGS_STRIPES_RANGE
+ */
+ __le32 stripes_min;
+ __le32 stripes_max;
+
+ __le64 unused[6];
+} __attribute__ ((__packed__));
+
+/*
+ * store balance parameters to disk so that balance can be properly
+ * resumed after crash or unmount
+ */
+struct btrfs_balance_item {
+ /* BTRFS_BALANCE_* */
+ __le64 flags;
+
+ struct btrfs_disk_balance_args data;
+ struct btrfs_disk_balance_args meta;
+ struct btrfs_disk_balance_args sys;
+
+ __le64 unused[4];
+} __attribute__ ((__packed__));
+
+#define BTRFS_FILE_EXTENT_INLINE 0
+#define BTRFS_FILE_EXTENT_REG 1
+#define BTRFS_FILE_EXTENT_PREALLOC 2
+
+struct btrfs_file_extent_item {
+ /*
+ * transaction id that created this extent
+ */
+ __le64 generation;
+ /*
+ * max number of bytes to hold this extent in ram
+ * when we split a compressed extent we can't know how big
+ * each of the resulting pieces will be. So, this is
+ * an upper limit on the size of the extent in ram instead of
+ * an exact limit.
+ */
+ __le64 ram_bytes;
+
+ /*
+ * 32 bits for the various ways we might encode the data,
+ * including compression and encryption. If any of these
+ * are set to something a given disk format doesn't understand
+ * it is treated like an incompat flag for reading and writing,
+ * but not for stat.
+ */
+ __u8 compression;
+ __u8 encryption;
+ __le16 other_encoding; /* spare for later use */
+
+ /* are we __inline__ data or a real extent? */
+ __u8 type;
+
+ /*
+ * disk space consumed by the extent, checksum blocks are included
+ * in these numbers
+ *
+ * At this offset in the structure, the __inline__ extent data start.
+ */
+ __le64 disk_bytenr;
+ __le64 disk_num_bytes;
+ /*
+ * the logical offset in file blocks (no csums)
+ * this extent record is for. This allows a file extent to point
+ * into the middle of an existing extent on disk, sharing it
+ * between two snapshots (useful if some bytes in the middle of the
+ * extent have changed
+ */
+ __le64 offset;
+ /*
+ * the logical number of file blocks (no csums included). This
+ * always reflects the size uncompressed and without encoding.
+ */
+ __le64 num_bytes;
+
+} __attribute__ ((__packed__));
+
+struct btrfs_csum_item {
+ __u8 csum;
+} __attribute__ ((__packed__));
+
+struct btrfs_dev_stats_item {
+ /*
+ * grow this item struct at the end for future enhancements and keep
+ * the existing values unchanged
+ */
+ __le64 values[BTRFS_DEV_STAT_VALUES_MAX];
+} __attribute__ ((__packed__));
+
+#define BTRFS_DEV_REPLACE_ITEM_CONT_READING_FROM_SRCDEV_MODE_ALWAYS 0
+#define BTRFS_DEV_REPLACE_ITEM_CONT_READING_FROM_SRCDEV_MODE_AVOID 1
+#define BTRFS_DEV_REPLACE_ITEM_STATE_NEVER_STARTED 0
+#define BTRFS_DEV_REPLACE_ITEM_STATE_STARTED 1
+#define BTRFS_DEV_REPLACE_ITEM_STATE_SUSPENDED 2
+#define BTRFS_DEV_REPLACE_ITEM_STATE_FINISHED 3
+#define BTRFS_DEV_REPLACE_ITEM_STATE_CANCELED 4
+
+struct btrfs_dev_replace_item {
+ /*
+ * grow this item struct at the end for future enhancements and keep
+ * the existing values unchanged
+ */
+ __le64 src_devid;
+ __le64 cursor_left;
+ __le64 cursor_right;
+ __le64 cont_reading_from_srcdev_mode;
+
+ __le64 replace_state;
+ __le64 time_started;
+ __le64 time_stopped;
+ __le64 num_write_errors;
+ __le64 num_uncorrectable_read_errors;
+} __attribute__ ((__packed__));
+
+/* different types of block groups (and chunks) */
+#define BTRFS_BLOCK_GROUP_DATA (1ULL << 0)
+#define BTRFS_BLOCK_GROUP_SYSTEM (1ULL << 1)
+#define BTRFS_BLOCK_GROUP_METADATA (1ULL << 2)
+#define BTRFS_BLOCK_GROUP_RAID0 (1ULL << 3)
+#define BTRFS_BLOCK_GROUP_RAID1 (1ULL << 4)
+#define BTRFS_BLOCK_GROUP_DUP (1ULL << 5)
+#define BTRFS_BLOCK_GROUP_RAID10 (1ULL << 6)
+#define BTRFS_BLOCK_GROUP_RAID5 (1ULL << 7)
+#define BTRFS_BLOCK_GROUP_RAID6 (1ULL << 8)
+#define BTRFS_BLOCK_GROUP_RESERVED (BTRFS_AVAIL_ALLOC_BIT_SINGLE | \
+ BTRFS_SPACE_INFO_GLOBAL_RSV)
+
+enum btrfs_raid_types {
+ BTRFS_RAID_RAID10,
+ BTRFS_RAID_RAID1,
+ BTRFS_RAID_DUP,
+ BTRFS_RAID_RAID0,
+ BTRFS_RAID_SINGLE,
+ BTRFS_RAID_RAID5,
+ BTRFS_RAID_RAID6,
+ BTRFS_NR_RAID_TYPES
+};
+
+#define BTRFS_BLOCK_GROUP_TYPE_MASK (BTRFS_BLOCK_GROUP_DATA | \
+ BTRFS_BLOCK_GROUP_SYSTEM | \
+ BTRFS_BLOCK_GROUP_METADATA)
+
+#define BTRFS_BLOCK_GROUP_PROFILE_MASK (BTRFS_BLOCK_GROUP_RAID0 | \
+ BTRFS_BLOCK_GROUP_RAID1 | \
+ BTRFS_BLOCK_GROUP_RAID5 | \
+ BTRFS_BLOCK_GROUP_RAID6 | \
+ BTRFS_BLOCK_GROUP_DUP | \
+ BTRFS_BLOCK_GROUP_RAID10)
+#define BTRFS_BLOCK_GROUP_RAID56_MASK (BTRFS_BLOCK_GROUP_RAID5 | \
+ BTRFS_BLOCK_GROUP_RAID6)
+
+/*
+ * We need a bit for restriper to be able to tell when chunks of type
+ * SINGLE are available. This "extended" profile format is used in
+ * fs_info->avail_*_alloc_bits (in-memory) and balance item fields
+ * (on-disk). The corresponding on-disk bit in chunk.type is reserved
+ * to avoid remappings between two formats in future.
+ */
+#define BTRFS_AVAIL_ALLOC_BIT_SINGLE (1ULL << 48)
+
+/*
+ * A fake block group type that is used to communicate global block reserve
+ * size to userspace via the SPACE_INFO ioctl.
+ */
+#define BTRFS_SPACE_INFO_GLOBAL_RSV (1ULL << 49)
+
+#define BTRFS_EXTENDED_PROFILE_MASK (BTRFS_BLOCK_GROUP_PROFILE_MASK | \
+ BTRFS_AVAIL_ALLOC_BIT_SINGLE)
+
+static __inline__ __u64 chunk_to_extended(__u64 flags)
+{
+ if ((flags & BTRFS_BLOCK_GROUP_PROFILE_MASK) == 0)
+ flags |= BTRFS_AVAIL_ALLOC_BIT_SINGLE;
+
+ return flags;
+}
+static __inline__ __u64 extended_to_chunk(__u64 flags)
+{
+ return flags & ~BTRFS_AVAIL_ALLOC_BIT_SINGLE;
+}
+
+struct btrfs_block_group_item {
+ __le64 used;
+ __le64 chunk_objectid;
+ __le64 flags;
+} __attribute__ ((__packed__));
+
+struct btrfs_free_space_info {
+ __le32 extent_count;
+ __le32 flags;
+} __attribute__ ((__packed__));
+
+#define BTRFS_FREE_SPACE_USING_BITMAPS (1ULL << 0)
+
+#define BTRFS_QGROUP_LEVEL_SHIFT 48
+static __inline__ __u64 btrfs_qgroup_level(__u64 qgroupid)
+{
+ return qgroupid >> BTRFS_QGROUP_LEVEL_SHIFT;
+}
+
+/*
+ * is subvolume quota turned on?
+ */
+#define BTRFS_QGROUP_STATUS_FLAG_ON (1ULL << 0)
+/*
+ * RESCAN is set during the initialization phase
+ */
+#define BTRFS_QGROUP_STATUS_FLAG_RESCAN (1ULL << 1)
+/*
+ * Some qgroup entries are known to be out of date,
+ * either because the configuration has changed in a way that
+ * makes a rescan necessary, or because the fs has been mounted
+ * with a non-qgroup-aware version.
+ * Turning qouta off and on again makes it inconsistent, too.
+ */
+#define BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT (1ULL << 2)
+
+#define BTRFS_QGROUP_STATUS_VERSION 1
+
+struct btrfs_qgroup_status_item {
+ __le64 version;
+ /*
+ * the generation is updated during every commit. As older
+ * versions of btrfs are not aware of qgroups, it will be
+ * possible to detect inconsistencies by checking the
+ * generation on mount time
+ */
+ __le64 generation;
+
+ /* flag definitions see above */
+ __le64 flags;
+
+ /*
+ * only used during scanning to record the progress
+ * of the scan. It contains a logical address
+ */
+ __le64 rescan;
+} __attribute__ ((__packed__));
+
+struct btrfs_qgroup_info_item {
+ __le64 generation;
+ __le64 rfer;
+ __le64 rfer_cmpr;
+ __le64 excl;
+ __le64 excl_cmpr;
+} __attribute__ ((__packed__));
+
+struct btrfs_qgroup_limit_item {
+ /*
+ * only updated when any of the other values change
+ */
+ __le64 flags;
+ __le64 max_rfer;
+ __le64 max_excl;
+ __le64 rsv_rfer;
+ __le64 rsv_excl;
+} __attribute__ ((__packed__));
+
+#endif /* _BTRFS_CTREE_H_ */
diff --git a/libbtrfsutil/btrfsutil.h b/libbtrfsutil/btrfsutil.h
new file mode 100644
index 00000000..6d655f49
--- /dev/null
+++ b/libbtrfsutil/btrfsutil.h
@@ -0,0 +1,650 @@
+/*
+ * Copyright (C) 2018 Facebook
+ *
+ * This file is part of libbtrfsutil.
+ *
+ * libbtrfsutil is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libbtrfsutil 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libbtrfsutil. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef BTRFS_UTIL_H
+#define BTRFS_UTIL_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/time.h>
+
+#define BTRFS_UTIL_VERSION_MAJOR 1
+#define BTRFS_UTIL_VERSION_MINOR 0
+#define BTRFS_UTIL_VERSION_PATCH 0
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * enum btrfs_util_error - libbtrfsutil error codes.
+ *
+ * All functions in libbtrfsutil that can return an error return this type and
+ * set errno.
+ */
+enum btrfs_util_error {
+ BTRFS_UTIL_OK,
+ BTRFS_UTIL_ERROR_STOP_ITERATION,
+ BTRFS_UTIL_ERROR_NO_MEMORY,
+ BTRFS_UTIL_ERROR_INVALID_ARGUMENT,
+ BTRFS_UTIL_ERROR_NOT_BTRFS,
+ BTRFS_UTIL_ERROR_NOT_SUBVOLUME,
+ BTRFS_UTIL_ERROR_SUBVOLUME_NOT_FOUND,
+ BTRFS_UTIL_ERROR_OPEN_FAILED,
+ BTRFS_UTIL_ERROR_RMDIR_FAILED,
+ BTRFS_UTIL_ERROR_UNLINK_FAILED,
+ BTRFS_UTIL_ERROR_STAT_FAILED,
+ BTRFS_UTIL_ERROR_STATFS_FAILED,
+ BTRFS_UTIL_ERROR_SEARCH_FAILED,
+ BTRFS_UTIL_ERROR_INO_LOOKUP_FAILED,
+ BTRFS_UTIL_ERROR_SUBVOL_GETFLAGS_FAILED,
+ BTRFS_UTIL_ERROR_SUBVOL_SETFLAGS_FAILED,
+ BTRFS_UTIL_ERROR_SUBVOL_CREATE_FAILED,
+ BTRFS_UTIL_ERROR_SNAP_CREATE_FAILED,
+ BTRFS_UTIL_ERROR_SNAP_DESTROY_FAILED,
+ BTRFS_UTIL_ERROR_DEFAULT_SUBVOL_FAILED,
+ BTRFS_UTIL_ERROR_SYNC_FAILED,
+ BTRFS_UTIL_ERROR_START_SYNC_FAILED,
+ BTRFS_UTIL_ERROR_WAIT_SYNC_FAILED,
+};
+
+/**
+ * btrfs_util_strerror() - Convert a libtrfsutil error code to a string
+ * description.
+ * @err: The error to convert.
+ *
+ * Return: Error description.
+ */
+const char *btrfs_util_strerror(enum btrfs_util_error err);
+
+/**
+ * btrfs_util_sync() - Force a sync on a specific Btrfs filesystem.
+ * @path: Path on a Btrfs filesystem.
+ *
+ * Return: %BTRFS_UTIL_OK on success, non-zero error code on failure.
+ */
+enum btrfs_util_error btrfs_util_sync(const char *path);
+
+/**
+ * btrfs_util_sync_fd() - See btrfs_util_sync().
+ */
+enum btrfs_util_error btrfs_util_sync_fd(int fd);
+
+/**
+ * btrfs_util_start_sync() - Start a sync on a specific Btrfs filesystem but
+ * don't wait for it.
+ * @path: Path on a Btrfs filesystem.
+ * @transid: Returned transaction ID which can be waited on with
+ * btrfs_util_wait_sync(). This can be %NULL.
+ *
+ * Return: %BTRFS_UTIL_OK on success, non-zero error code on failure.
+ */
+enum btrfs_util_error btrfs_util_start_sync(const char *path,
+ uint64_t *transid);
+
+/**
+ * btrfs_util_start_sync_fd() - See btrfs_util_start_sync().
+ */
+enum btrfs_util_error btrfs_util_start_sync_fd(int fd, uint64_t *transid);
+
+/**
+ * btrfs_util_wait_sync() - Wait for a transaction with a given ID to sync.
+ * @path: Path on a Btrfs filesystem.
+ * @transid: Transaction ID to wait for, or zero for the current transaction.
+ *
+ * Return: %BTRFS_UTIL_OK on success, non-zero error code on failure.
+ */
+enum btrfs_util_error btrfs_util_wait_sync(const char *path, uint64_t transid);
+
+/**
+ * btrfs_util_wait_sync_fd() - See btrfs_util_wait_sync().
+ */
+enum btrfs_util_error btrfs_util_wait_sync_fd(int fd, uint64_t transid);
+
+/**
+ * btrfs_util_is_subvolume() - Return whether a given path is a Btrfs subvolume.
+ * @path: Path to check.
+ *
+ * Return: %BTRFS_UTIL_OK if @path is a Btrfs subvolume,
+ * %BTRFS_UTIL_ERROR_NOT_BTRFS if @path is not on a Btrfs filesystem,
+ * %BTRFS_UTIL_ERROR_NOT_SUBVOLUME if @path is not a subvolume, non-zero error
+ * code on any other failure.
+ */
+enum btrfs_util_error btrfs_util_is_subvolume(const char *path);
+
+/**
+ * btrfs_util_is_subvolume_fd() - See btrfs_util_is_subvolume().
+ */
+enum btrfs_util_error btrfs_util_is_subvolume_fd(int fd);
+
+/**
+ * btrfs_util_subvolume_id() - Get the ID of the subvolume containing a path.
+ * @path: Path on a Btrfs filesystem.
+ * @id_ret: Returned subvolume ID.
+ *
+ * Return: %BTRFS_UTIL_OK on success, non-zero error code on failure.
+ */
+enum btrfs_util_error btrfs_util_subvolume_id(const char *path,
+ uint64_t *id_ret);
+
+/**
+ * btrfs_util_subvolume_id_fd() - See btrfs_util_subvolume_id().
+ */
+enum btrfs_util_error btrfs_util_subvolume_id_fd(int fd, uint64_t *id_ret);
+
+/**
+ * btrfs_util_subvolume_path() - Get the path of the subvolume with a given ID
+ * relative to the filesystem root.
+ * @path: Path on a Btrfs filesystem.
+ * @id: ID of subvolume to set as the default. If zero is given, the subvolume
+ * ID of @path is used.
+ * @path_ret: Returned path.
+ *
+ * This requires appropriate privilege (CAP_SYS_ADMIN).
+ *
+ * Return: %BTRFS_UTIL_OK on success, non-zero error code on failure.
+ */
+enum btrfs_util_error btrfs_util_subvolume_path(const char *path, uint64_t id,
+ char **path_ret);
+
+/**
+ * btrfs_util_subvolume_path_fd() - See btrfs_util_subvolume_path().
+ */
+enum btrfs_util_error btrfs_util_subvolume_path_fd(int fd, uint64_t id,
+ char **path_ret);
+
+/**
+ * struct btrfs_util_subvolume_info - Information about a Btrfs subvolume.
+ */
+struct btrfs_util_subvolume_info {
+ /** @id: ID of this subvolume, unique across the filesystem. */
+ uint64_t id;
+
+ /**
+ * @parent_id: ID of the subvolume which contains this subvolume, or
+ * zero for the root subvolume (BTRFS_FS_TREE_OBJECTID) or orphaned
+ * subvolumes (i.e., subvolumes which have been deleted but not yet
+ * cleaned up).
+ */
+ uint64_t parent_id;
+
+ /**
+ * @dir_id: Inode number of the directory containing this subvolume in
+ * the parent subvolume, or zero for the root subvolume
+ * (BTRFS_FS_TREE_OBJECTID) or orphaned subvolumes.
+ */
+ uint64_t dir_id;
+
+ /** @flags: On-disk root item flags. */
+ uint64_t flags;
+
+ /** @uuid: UUID of this subvolume. */
+ uint8_t uuid[16];
+
+ /**
+ * @parent_uuid: UUID of the subvolume this subvolume is a snapshot of,
+ * or all zeroes if this subvolume is not a snapshot.
+ */
+ uint8_t parent_uuid[16];
+
+ /**
+ * @received_uuid: UUID of the subvolume this subvolume was received
+ * from, or all zeroes if this subvolume was not received. Note that
+ * this field, @stransid, @rtransid, @stime, and @rtime are set manually
+ * by userspace after a subvolume is received.
+ */
+ uint8_t received_uuid[16];
+
+ /** @generation: Transaction ID of the subvolume root. */
+ uint64_t generation;
+
+ /**
+ * @ctransid: Transaction ID when an inode in this subvolume was last
+ * changed.
+ */
+ uint64_t ctransid;
+
+ /** @otransid: Transaction ID when this subvolume was created. */
+ uint64_t otransid;
+
+ /**
+ * @stransid: Transaction ID of the sent subvolume this subvolume was
+ * received from, or zero if this subvolume was not received. See the
+ * note on @received_uuid.
+ */
+ uint64_t stransid;
+
+ /**
+ * @rtransid: Transaction ID when this subvolume was received, or zero
+ * if this subvolume was not received. See the note on @received_uuid.
+ */
+ uint64_t rtransid;
+
+ /** @ctime: Time when an inode in this subvolume was last changed. */
+ struct timespec ctime;
+
+ /** @otime: Time when this subvolume was created. */
+ struct timespec otime;
+
+ /**
+ * @stime: Not well-defined, usually zero unless it was set otherwise.
+ * See the note on @received_uuid.
+ */
+ struct timespec stime;
+
+ /**
+ * @rtime: Time when this subvolume was received, or zero if this
+ * subvolume was not received. See the note on @received_uuid.
+ */
+ struct timespec rtime;
+};
+
+/**
+ * btrfs_util_subvolume_info() - Get information about a subvolume.
+ * @path: Path in a Btrfs filesystem. This may be any path in the filesystem; it
+ * does not have to refer to a subvolume unless @id is zero.
+ * @id: ID of subvolume to get information about. If zero is given, the
+ * subvolume ID of @path is used.
+ * @subvol: Returned subvolume information. This can be %NULL if you just want
+ * to check whether the subvolume exists; %BTRFS_UTIL_ERROR_SUBVOLUME_NOT_FOUND
+ * will be returned if it does not.
+ *
+ * This requires appropriate privilege (CAP_SYS_ADMIN).
+ *
+ * Return: %BTRFS_UTIL_OK on success, non-zero error code on failure.
+ */
+enum btrfs_util_error btrfs_util_subvolume_info(const char *path, uint64_t id,
+ struct btrfs_util_subvolume_info *subvol);
+
+/**
+ * btrfs_util_subvolume_info_fd() - See btrfs_util_subvolume_info().
+ */
+enum btrfs_util_error btrfs_util_subvolume_info_fd(int fd, uint64_t id,
+ struct btrfs_util_subvolume_info *subvol);
+
+/**
+ * btrfs_util_get_subvolume_read_only() - Get whether a subvolume is read-only.
+ * @path: Subvolume path.
+ * @ret: Returned read-only flag.
+ *
+ * Return: %BTRFS_UTIL_OK on success, non-zero error code on failure.
+ */
+enum btrfs_util_error btrfs_util_get_subvolume_read_only(const char *path,
+ bool *ret);
+
+/**
+ * btrfs_util_get_subvolume_read_only_fd() - See
+ * btrfs_util_get_subvolume_read_only().
+ */
+enum btrfs_util_error btrfs_util_get_subvolume_read_only_fd(int fd, bool *ret);
+
+/**
+ * btrfs_util_set_subvolume_read_only() - Set whether a subvolume is read-only.
+ * @path: Subvolume path.
+ * @read_only: New value of read-only flag.
+ *
+ * This requires appropriate privilege (CAP_SYS_ADMIN).
+ *
+ * Return: %BTRFS_UTIL_OK on success, non-zero error code on failure.
+ */
+enum btrfs_util_error btrfs_util_set_subvolume_read_only(const char *path,
+ bool read_only);
+
+/**
+ * btrfs_util_set_subvolume_read_only_fd() - See
+ * btrfs_util_set_subvolume_read_only().
+ */
+enum btrfs_util_error btrfs_util_set_subvolume_read_only_fd(int fd,
+ bool read_only);
+
+/**
+ * btrfs_util_get_default_subvolume() - Get the default subvolume for a
+ * filesystem.
+ * @path: Path on a Btrfs filesystem.
+ * @id_ret: Returned subvolume ID.
+ *
+ * This requires appropriate privilege (CAP_SYS_ADMIN).
+ *
+ * Return: %BTRFS_UTIL_OK on success, non-zero error code on failure.
+ */
+enum btrfs_util_error btrfs_util_get_default_subvolume(const char *path,
+ uint64_t *id_ret);
+
+/**
+ * btrfs_util_get_default_subvolume_fd() - See
+ * btrfs_util_get_default_subvolume().
+ */
+enum btrfs_util_error btrfs_util_get_default_subvolume_fd(int fd,
+ uint64_t *id_ret);
+
+/**
+ * btrfs_util_set_default_subvolume() - Set the default subvolume for a
+ * filesystem.
+ * @path: Path in a Btrfs filesystem. This may be any path in the filesystem; it
+ * does not have to refer to a subvolume unless @id is zero.
+ * @id: ID of subvolume to set as the default. If zero is given, the subvolume
+ * ID of @path is used.
+ *
+ * This requires appropriate privilege (CAP_SYS_ADMIN).
+ *
+ * Return: %BTRFS_UTIL_OK on success, non-zero error code on failure.
+ */
+enum btrfs_util_error btrfs_util_set_default_subvolume(const char *path,
+ uint64_t id);
+
+/**
+ * btrfs_util_set_default_subvolume_fd() - See
+ * btrfs_util_set_default_subvolume().
+ */
+enum btrfs_util_error btrfs_util_set_default_subvolume_fd(int fd, uint64_t id);
+
+struct btrfs_util_qgroup_inherit;
+
+/**
+ * btrfs_util_create_subvolume() - Create a new subvolume.
+ * @path: Where to create the subvolume.
+ * @flags: Must be zero.
+ * @async_transid: If not NULL, create the subvolume asynchronously (i.e.,
+ * without waiting for it to commit it to disk) and return the transaction ID
+ * that it was created in. This transaction ID can be waited on with
+ * btrfs_util_wait_sync().
+ * @qgroup_inherit: Qgroups to inherit from, or NULL.
+ *
+ * Return: %BTRFS_UTIL_OK on success, non-zero error code on failure.
+ */
+enum btrfs_util_error btrfs_util_create_subvolume(const char *path, int flags,
+ uint64_t *async_transid,
+ struct btrfs_util_qgroup_inherit *qgroup_inherit);
+
+/**
+ * btrfs_util_create_subvolume_fd() - Create a new subvolume given its parent
+ * and name.
+ * @parent_fd: File descriptor of the parent directory where the subvolume
+ * should be created.
+ * @name: Name of the subvolume to create.
+ * @flags: See btrfs_util_create_subvolume().
+ * @async_transid: See btrfs_util_create_subvolume().
+ * @qgroup_inherit: See btrfs_util_create_subvolume().
+ *
+ * Return: %BTRFS_UTIL_OK on success, non-zero error code on failure.
+ */
+enum btrfs_util_error btrfs_util_create_subvolume_fd(int parent_fd,
+ const char *name,
+ int flags,
+ uint64_t *async_transid,
+ struct btrfs_util_qgroup_inherit *qgroup_inherit);
+
+/**
+ * BTRFS_UTIL_CREATE_SNAPSHOT_RECURSIVE - Also snapshot subvolumes beneath the
+ * source subvolume onto the same location on the new snapshot.
+ *
+ * Note that this is currently implemented in userspace non-atomically. Because
+ * it modifies the newly-created snapshot, it cannot be combined with
+ * %BTRFS_UTIL_CREATE_SNAPSHOT_READ_ONLY. It requires appropriate privilege
+ * (CAP_SYS_ADMIN).
+ */
+#define BTRFS_UTIL_CREATE_SNAPSHOT_RECURSIVE (1 << 0)
+/**
+ * BTRFS_UTIL_CREATE_SNAPSHOT_READ_ONLY - Create a read-only snapshot.
+ */
+#define BTRFS_UTIL_CREATE_SNAPSHOT_READ_ONLY (1 << 1)
+#define BTRFS_UTIL_CREATE_SNAPSHOT_MASK ((1 << 2) - 1)
+
+/**
+ * btrfs_util_create_snapshot() - Create a new snapshot from a source subvolume
+ * path.
+ * @source: Path of the existing subvolume to snapshot.
+ * @path: Where to create the snapshot.
+ * @flags: Bitmask of BTRFS_UTIL_CREATE_SNAPSHOT_* flags.
+ * @async_transid: See btrfs_util_create_subvolume(). If
+ * %BTRFS_UTIL_CREATE_SNAPSHOT_RECURSIVE was in @flags, then this will contain
+ * the largest transaction ID of all created subvolumes.
+ * @qgroup_inherit: See btrfs_util_create_subvolume().
+ *
+ * Return: %BTRFS_UTIL_OK on success, non-zero error code on failure.
+ */
+enum btrfs_util_error btrfs_util_create_snapshot(const char *source,
+ const char *path, int flags,
+ uint64_t *async_transid,
+ struct btrfs_util_qgroup_inherit *qgroup_inherit);
+
+/**
+ * btrfs_util_create_snapshot_fd() - See btrfs_util_create_snapshot().
+ */
+enum btrfs_util_error btrfs_util_create_snapshot_fd(int fd, const char *path,
+ int flags,
+ uint64_t *async_transid,
+ struct btrfs_util_qgroup_inherit *qgroup_inherit);
+
+/**
+ * btrfs_util_create_snapshot_fd2() - Create a new snapshot from a source
+ * subvolume file descriptor and a target parent file descriptor and name.
+ * @fd: File descriptor of the existing subvolume to snapshot.
+ * @parent_fd: File descriptor of the parent directory where the snapshot should
+ * be created.
+ * @name: Name of the snapshot to create.
+ * @flags: See btrfs_util_create_snapshot().
+ * @async_transid: See btrfs_util_create_snapshot().
+ * @qgroup_inherit: See btrfs_util_create_snapshot().
+ */
+enum btrfs_util_error btrfs_util_create_snapshot_fd2(int fd, int parent_fd,
+ const char *name,
+ int flags,
+ uint64_t *async_transid,
+ struct btrfs_util_qgroup_inherit *qgroup_inherit);
+
+/**
+ * BTRFS_UTIL_DELETE_SUBVOLUME_RECURSIVE - Delete subvolumes beneath the given
+ * subvolume before attempting to delete the given subvolume.
+ *
+ * If this flag is not used, deleting a subvolume with child subvolumes is an
+ * error. Note that this is currently implemented in userspace non-atomically.
+ * It requires appropriate privilege (CAP_SYS_ADMIN).
+ */
+#define BTRFS_UTIL_DELETE_SUBVOLUME_RECURSIVE (1 << 0)
+#define BTRFS_UTIL_DELETE_SUBVOLUME_MASK ((1 << 1) - 1)
+
+/**
+ * btrfs_util_delete_subvolume() - Delete a subvolume or snapshot.
+ * @path: Path of the subvolume to delete.
+ * @flags: Bitmask of BTRFS_UTIL_DELETE_SUBVOLUME_* flags.
+ *
+ * Return: %BTRFS_UTIL_OK on success, non-zero error code on failure.
+ */
+enum btrfs_util_error btrfs_util_delete_subvolume(const char *path, int flags);
+
+/**
+ * btrfs_util_delete_subvolume_fd() - Delete a subvolume or snapshot given its
+ * parent and name.
+ * @parent_fd: File descriptor of the subvolume's parent directory.
+ * @name: Name of the subvolume.
+ * @flags: See btrfs_util_delete_subvolume().
+ *
+ * Return: %BTRFS_UTIL_OK on success, non-zero error code on failure.
+ */
+enum btrfs_util_error btrfs_util_delete_subvolume_fd(int parent_fd,
+ const char *name,
+ int flags);
+
+struct btrfs_util_subvolume_iterator;
+
+/**
+ * BTRFS_UTIL_SUBVOLUME_ITERATOR_POST_ORDER - Iterate post-order. The default
+ * behavior is pre-order, e.g., foo will be yielded before foo/bar. If this flag
+ * is specified, foo/bar will be yielded before foo.
+ */
+#define BTRFS_UTIL_SUBVOLUME_ITERATOR_POST_ORDER (1 << 0)
+#define BTRFS_UTIL_SUBVOLUME_ITERATOR_MASK ((1 << 1) - 1)
+
+/**
+ * btrfs_util_create_subvolume_iterator() - Create an iterator over subvolumes
+ * in a Btrfs filesystem.
+ * @path: Path in a Btrfs filesystem. This may be any path in the filesystem; it
+ * does not have to refer to a subvolume unless @top is zero.
+ * @top: List subvolumes beneath (but not including) the subvolume with this ID.
+ * If zero is given, the subvolume ID of @path is used. To list all subvolumes,
+ * pass %BTRFS_FS_TREE_OBJECTID (i.e., 5). The returned paths are relative to
+ * the subvolume with this ID.
+ * @flags: Bitmask of BTRFS_UTIL_SUBVOLUME_ITERATOR_* flags.
+ * @ret: Returned iterator.
+ *
+ * The returned iterator must be freed with
+ * btrfs_util_destroy_subvolume_iterator().
+ *
+ * Return: %BTRFS_UTIL_OK on success, non-zero error code on failure.
+ */
+enum btrfs_util_error btrfs_util_create_subvolume_iterator(const char *path,
+ uint64_t top,
+ int flags,
+ struct btrfs_util_subvolume_iterator **ret);
+
+/**
+ * btrfs_util_create_subvolume_iterator_fd() - See
+ * btrfs_util_create_subvolume_iterator().
+ */
+enum btrfs_util_error btrfs_util_create_subvolume_iterator_fd(int fd,
+ uint64_t top,
+ int flags,
+ struct btrfs_util_subvolume_iterator **ret);
+
+/**
+ * btrfs_util_destroy_subvolume_iterator() - Destroy a subvolume iterator
+ * previously created by btrfs_util_create_subvolume_iterator().
+ * @iter: Iterator to destroy.
+ */
+void btrfs_util_destroy_subvolume_iterator(struct btrfs_util_subvolume_iterator *iter);
+
+/**
+ * btrfs_util_subvolume_iterator_fd() - Get the file descriptor associated with
+ * a subvolume iterator.
+ * @iter: Iterator to get.
+ *
+ * This can be used to get the file descriptor opened by
+ * btrfs_util_create_subvolume_iterator() in order to use it for other
+ * functions.
+ *
+ * Return: File descriptor.
+ */
+int btrfs_util_subvolume_iterator_fd(const struct btrfs_util_subvolume_iterator *iter);
+
+/**
+ * btrfs_util_subvolume_iterator_next() - Get the next subvolume from a
+ * subvolume iterator.
+ * @iter: Subvolume iterator.
+ * @path_ret: Returned subvolume path, relative to the subvolume ID used to
+ * create the iterator. May be %NULL.
+ * Must be freed with free().
+ * @id_ret: Returned subvolume ID. May be %NULL.
+ *
+ * This requires appropriate privilege (CAP_SYS_ADMIN).
+ *
+ * Return: %BTRFS_UTIL_OK on success, %BTRFS_UTIL_ERROR_STOP_ITERATION if there
+ * are no more subvolumes, non-zero error code on failure.
+ */
+enum btrfs_util_error btrfs_util_subvolume_iterator_next(struct btrfs_util_subvolume_iterator *iter,
+ char **path_ret,
+ uint64_t *id_ret);
+
+/**
+ * btrfs_util_subvolume_iterator_next_info() - Get information about the next
+ * subvolume for a subvolume iterator.
+ * @iter: Subvolume iterator.
+ * @path_ret: See btrfs_util_subvolume_iterator_next().
+ * @subvol: Returned subvolume information.
+ *
+ * This convenience function basically combines
+ * btrfs_util_subvolume_iterator_next() and btrfs_util_subvolume_info().
+ *
+ * This requires appropriate privilege (CAP_SYS_ADMIN).
+ *
+ * Return: See btrfs_util_subvolume_iterator_next().
+ */
+enum btrfs_util_error btrfs_util_subvolume_iterator_next_info(struct btrfs_util_subvolume_iterator *iter,
+ char **path_ret,
+ struct btrfs_util_subvolume_info *subvol);
+
+/**
+ * btrfs_util_deleted_subvolumes() - Get a list of subvolume which have been
+ * deleted but not yet cleaned up.
+ * @path: Path on a Btrfs filesystem.
+ * @ids: Returned array of subvolume IDs.
+ * @n: Returned number of IDs in the @ids array.
+ *
+ * This requires appropriate privilege (CAP_SYS_ADMIN).
+ *
+ * Return: %BTRFS_UTIL_OK on success, non-zero error code on failure.
+ */
+enum btrfs_util_error btrfs_util_deleted_subvolumes(const char *path,
+ uint64_t **ids,
+ size_t *n);
+
+/**
+ * btrfs_util_deleted_subvolumes_fd() - See btrfs_util_deleted_subvolumes().
+ */
+enum btrfs_util_error btrfs_util_deleted_subvolumes_fd(int fd, uint64_t **ids,
+ size_t *n);
+
+/**
+ * btrfs_util_create_qgroup_inherit() - Create a qgroup inheritance specifier
+ * for btrfs_util_create_subvolume() or btrfs_util_create_snapshot().
+ * @flags: Must be zero.
+ * @ret: Returned qgroup inheritance specifier.
+ *
+ * The returned structure must be freed with
+ * btrfs_util_destroy_qgroup_inherit().
+ *
+ * Return: %BTRFS_UTIL_OK on success, non-zero error code on failure.
+ */
+enum btrfs_util_error btrfs_util_create_qgroup_inherit(int flags,
+ struct btrfs_util_qgroup_inherit **ret);
+
+/**
+ * btrfs_util_destroy_qgroup_inherit() - Destroy a qgroup inheritance specifier
+ * previously created with btrfs_util_create_qgroup_inherit().
+ * @inherit: Specifier to destroy.
+ */
+void btrfs_util_destroy_qgroup_inherit(struct btrfs_util_qgroup_inherit *inherit);
+
+/**
+ * btrfs_util_qgroup_inherit_add_group() - Add inheritance from a qgroup to a
+ * qgroup inheritance specifier.
+ * @inherit: Specifier to modify. May be reallocated.
+ * @qgroupid: ID of qgroup to inherit from.
+ *
+ * Return: %BTRFS_UTIL_OK on success, non-zero error code on failure.
+ */
+enum btrfs_util_error btrfs_util_qgroup_inherit_add_group(struct btrfs_util_qgroup_inherit **inherit,
+ uint64_t qgroupid);
+
+/**
+ * btrfs_util_qgroup_inherit_get_groups() - Get the qgroups a qgroup inheritance
+ * specifier contains.
+ * @inherit: Qgroup inheritance specifier.
+ * @groups: Returned array of qgroup IDs.
+ * @n: Returned number of entries in the @groups array.
+ */
+void btrfs_util_qgroup_inherit_get_groups(const struct btrfs_util_qgroup_inherit *inherit,
+ const uint64_t **groups, size_t *n);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* BTRFS_UTIL_H */
diff --git a/libbtrfsutil/btrfsutil_internal.h b/libbtrfsutil/btrfsutil_internal.h
new file mode 100644
index 00000000..8bbf01f4
--- /dev/null
+++ b/libbtrfsutil/btrfsutil_internal.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018 Facebook
+ *
+ * This file is part of libbtrfsutil.
+ *
+ * libbtrfsutil is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libbtrfsutil 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libbtrfsutil. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef BTRFS_UTIL_INTERNAL_H
+#define BTRFS_UTIL_INTERNAL_H
+
+#include <asm/byteorder.h>
+
+#include "btrfsutil.h"
+#include "btrfs.h"
+#include "btrfs_tree.h"
+
+#define PUBLIC __attribute__((visibility("default")))
+
+#define le16_to_cpu __le16_to_cpu
+#define le32_to_cpu __le32_to_cpu
+#define le64_to_cpu __le64_to_cpu
+
+#define SAVE_ERRNO_AND_CLOSE(fd) do { \
+ int saved_errno = errno; \
+ \
+ close(fd); \
+ errno = saved_errno; \
+} while (0)
+
+#endif /* BTRFS_UTIL_INTERNAL_H */
diff --git a/libbtrfsutil/errors.c b/libbtrfsutil/errors.c
new file mode 100644
index 00000000..634edc65
--- /dev/null
+++ b/libbtrfsutil/errors.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2018 Facebook
+ *
+ * This file is part of libbtrfsutil.
+ *
+ * libbtrfsutil is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libbtrfsutil 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libbtrfsutil. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stddef.h>
+
+#include "btrfsutil_internal.h"
+
+static const char * const error_messages[] = {
+ [BTRFS_UTIL_OK] = "Success",
+ [BTRFS_UTIL_ERROR_STOP_ITERATION] = "Stop iteration",
+ [BTRFS_UTIL_ERROR_NO_MEMORY] = "Cannot allocate memory",
+ [BTRFS_UTIL_ERROR_INVALID_ARGUMENT] = "Invalid argument",
+ [BTRFS_UTIL_ERROR_NOT_BTRFS] = "Not a Btrfs filesystem",
+ [BTRFS_UTIL_ERROR_NOT_SUBVOLUME] = "Not a Btrfs subvolume",
+ [BTRFS_UTIL_ERROR_SUBVOLUME_NOT_FOUND] = "Subvolume not found",
+ [BTRFS_UTIL_ERROR_OPEN_FAILED] = "Could not open",
+ [BTRFS_UTIL_ERROR_RMDIR_FAILED] = "Could not rmdir",
+ [BTRFS_UTIL_ERROR_UNLINK_FAILED] = "Could not unlink",
+ [BTRFS_UTIL_ERROR_STAT_FAILED] = "Could not stat",
+ [BTRFS_UTIL_ERROR_STATFS_FAILED] = "Could not statfs",
+ [BTRFS_UTIL_ERROR_SEARCH_FAILED] = "Could not search B-tree",
+ [BTRFS_UTIL_ERROR_INO_LOOKUP_FAILED] = "Could not lookup inode",
+ [BTRFS_UTIL_ERROR_SUBVOL_GETFLAGS_FAILED] = "Could not get subvolume flags",
+ [BTRFS_UTIL_ERROR_SUBVOL_SETFLAGS_FAILED] = "Could not set subvolume flags",
+ [BTRFS_UTIL_ERROR_SUBVOL_CREATE_FAILED] = "Could not create subvolume",
+ [BTRFS_UTIL_ERROR_SNAP_CREATE_FAILED] = "Could not create snapshot",
+ [BTRFS_UTIL_ERROR_SNAP_DESTROY_FAILED] = "Could not destroy subvolume/snapshot",
+ [BTRFS_UTIL_ERROR_DEFAULT_SUBVOL_FAILED] = "Could not set default subvolume",
+ [BTRFS_UTIL_ERROR_SYNC_FAILED] = "Could not sync filesystem",
+ [BTRFS_UTIL_ERROR_START_SYNC_FAILED] = "Could not start filesystem sync",
+ [BTRFS_UTIL_ERROR_WAIT_SYNC_FAILED] = "Could not wait for filesystem sync",
+};
+
+PUBLIC const char *btrfs_util_strerror(enum btrfs_util_error err)
+{
+ if (err < 0 || err >= sizeof(error_messages) / sizeof(error_messages[0]))
+ return NULL;
+ return error_messages[err];
+}
diff --git a/libbtrfsutil/filesystem.c b/libbtrfsutil/filesystem.c
new file mode 100644
index 00000000..9c02b124
--- /dev/null
+++ b/libbtrfsutil/filesystem.c
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2018 Facebook
+ *
+ * This file is part of libbtrfsutil.
+ *
+ * libbtrfsutil is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libbtrfsutil 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libbtrfsutil. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+
+#include "btrfsutil_internal.h"
+
+PUBLIC enum btrfs_util_error btrfs_util_sync(const char *path)
+{
+ enum btrfs_util_error err;
+ int fd;
+
+ fd = open(path, O_RDONLY);
+ if (fd == -1)
+ return BTRFS_UTIL_ERROR_OPEN_FAILED;
+
+ err = btrfs_util_sync_fd(fd);
+ SAVE_ERRNO_AND_CLOSE(fd);
+ return err;
+}
+
+PUBLIC enum btrfs_util_error btrfs_util_sync_fd(int fd)
+{
+ int ret;
+
+ ret = ioctl(fd, BTRFS_IOC_SYNC, NULL);
+ if (ret == -1)
+ return BTRFS_UTIL_ERROR_SYNC_FAILED;
+
+ return BTRFS_UTIL_OK;
+}
+
+PUBLIC enum btrfs_util_error btrfs_util_start_sync(const char *path,
+ uint64_t *transid)
+{
+ enum btrfs_util_error err;
+ int fd;
+
+ fd = open(path, O_RDONLY);
+ if (fd == -1)
+ return BTRFS_UTIL_ERROR_OPEN_FAILED;
+
+ err = btrfs_util_start_sync_fd(fd, transid);
+ SAVE_ERRNO_AND_CLOSE(fd);
+ return err;
+}
+
+PUBLIC enum btrfs_util_error btrfs_util_start_sync_fd(int fd, uint64_t *transid)
+{
+ int ret;
+
+ ret = ioctl(fd, BTRFS_IOC_START_SYNC, transid);
+ if (ret == -1)
+ return BTRFS_UTIL_ERROR_START_SYNC_FAILED;
+
+ return BTRFS_UTIL_OK;
+}
+
+PUBLIC enum btrfs_util_error btrfs_util_wait_sync(const char *path,
+ uint64_t transid)
+{
+ enum btrfs_util_error err;
+ int fd;
+
+ fd = open(path, O_RDONLY);
+ if (fd == -1)
+ return BTRFS_UTIL_ERROR_OPEN_FAILED;
+
+ err = btrfs_util_wait_sync_fd(fd, transid);
+ SAVE_ERRNO_AND_CLOSE(fd);
+ return err;
+}
+
+PUBLIC enum btrfs_util_error btrfs_util_wait_sync_fd(int fd, uint64_t transid)
+{
+ int ret;
+
+ ret = ioctl(fd, BTRFS_IOC_WAIT_SYNC, &transid);
+ if (ret == -1)
+ return BTRFS_UTIL_ERROR_WAIT_SYNC_FAILED;
+
+ return BTRFS_UTIL_OK;
+}
diff --git a/libbtrfsutil/python/.gitignore b/libbtrfsutil/python/.gitignore
new file mode 100644
index 00000000..d050ff7c
--- /dev/null
+++ b/libbtrfsutil/python/.gitignore
@@ -0,0 +1,7 @@
+__pycache__
+*.pyc
+/btrfsutil.egg-info
+/btrfsutil*.so
+/build
+/constants.c
+/dist
diff --git a/libbtrfsutil/python/btrfsutilpy.h b/libbtrfsutil/python/btrfsutilpy.h
new file mode 100644
index 00000000..af3a6edf
--- /dev/null
+++ b/libbtrfsutil/python/btrfsutilpy.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2018 Facebook
+ *
+ * This file is part of libbtrfsutil.
+ *
+ * libbtrfsutil is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libbtrfsutil 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libbtrfsutil. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef BTRFSUTILPY_H
+#define BTRFSUTILPY_H
+
+#define PY_SSIZE_T_CLEAN
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <Python.h>
+#include "structmember.h"
+
+#include <btrfsutil.h>
+
+typedef struct {
+ PyObject_HEAD
+ struct btrfs_util_qgroup_inherit *inherit;
+} QgroupInherit;
+
+extern PyTypeObject BtrfsUtilError_type;
+extern PyStructSequence_Desc SubvolumeInfo_desc;
+extern PyTypeObject SubvolumeInfo_type;
+extern PyTypeObject SubvolumeIterator_type;
+extern PyTypeObject QgroupInherit_type;
+
+/*
+ * Helpers for path arguments based on posixmodule.c in CPython.
+ */
+struct path_arg {
+ bool allow_fd;
+ char *path;
+ int fd;
+ Py_ssize_t length;
+ PyObject *object;
+ PyObject *cleanup;
+};
+int path_converter(PyObject *o, void *p);
+void path_cleanup(struct path_arg *path);
+
+PyObject *list_from_uint64_array(const uint64_t *arr, size_t n);
+
+void SetFromBtrfsUtilError(enum btrfs_util_error err);
+void SetFromBtrfsUtilErrorWithPath(enum btrfs_util_error err,
+ struct path_arg *path);
+void SetFromBtrfsUtilErrorWithPaths(enum btrfs_util_error err,
+ struct path_arg *path1,
+ struct path_arg *path2);
+
+PyObject *filesystem_sync(PyObject *self, PyObject *args, PyObject *kwds);
+PyObject *start_sync(PyObject *self, PyObject *args, PyObject *kwds);
+PyObject *wait_sync(PyObject *self, PyObject *args, PyObject *kwds);
+PyObject *is_subvolume(PyObject *self, PyObject *args, PyObject *kwds);
+PyObject *subvolume_id(PyObject *self, PyObject *args, PyObject *kwds);
+PyObject *subvolume_path(PyObject *self, PyObject *args, PyObject *kwds);
+PyObject *subvolume_info(PyObject *self, PyObject *args, PyObject *kwds);
+PyObject *get_subvolume_read_only(PyObject *self, PyObject *args, PyObject *kwds);
+PyObject *set_subvolume_read_only(PyObject *self, PyObject *args, PyObject *kwds);
+PyObject *get_default_subvolume(PyObject *self, PyObject *args, PyObject *kwds);
+PyObject *set_default_subvolume(PyObject *self, PyObject *args, PyObject *kwds);
+PyObject *create_subvolume(PyObject *self, PyObject *args, PyObject *kwds);
+PyObject *create_snapshot(PyObject *self, PyObject *args, PyObject *kwds);
+PyObject *delete_subvolume(PyObject *self, PyObject *args, PyObject *kwds);
+PyObject *deleted_subvolumes(PyObject *self, PyObject *args, PyObject *kwds);
+
+void add_module_constants(PyObject *m);
+
+#endif /* BTRFSUTILPY_H */
diff --git a/libbtrfsutil/python/error.c b/libbtrfsutil/python/error.c
new file mode 100644
index 00000000..0876c9b4
--- /dev/null
+++ b/libbtrfsutil/python/error.c
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2018 Facebook
+ *
+ * This file is part of libbtrfsutil.
+ *
+ * libbtrfsutil is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libbtrfsutil 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libbtrfsutil. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "btrfsutilpy.h"
+
+typedef struct {
+ PyOSErrorObject os_error;
+ PyObject *btrfsutilerror;
+} BtrfsUtilError;
+
+void SetFromBtrfsUtilError(enum btrfs_util_error err)
+{
+ SetFromBtrfsUtilErrorWithPaths(err, NULL, NULL);
+}
+
+void SetFromBtrfsUtilErrorWithPath(enum btrfs_util_error err,
+ struct path_arg *path1)
+{
+ SetFromBtrfsUtilErrorWithPaths(err, path1, NULL);
+}
+
+void SetFromBtrfsUtilErrorWithPaths(enum btrfs_util_error err,
+ struct path_arg *path1,
+ struct path_arg *path2)
+{
+ PyObject *strobj, *args, *exc;
+ int i = errno;
+ const char *str1 = btrfs_util_strerror(err), *str2 = strerror(i);
+
+ if (str1 && str2 && strcmp(str1, str2) != 0) {
+ strobj = PyUnicode_FromFormat("%s: %s", str1, str2);
+ } else if (str1) {
+ strobj = PyUnicode_FromString(str1);
+ } else if (str2) {
+ strobj = PyUnicode_FromString(str2);
+ } else {
+ Py_INCREF(Py_None);
+ strobj = Py_None;
+ }
+ if (strobj == NULL)
+ return;
+
+ args = Py_BuildValue("iOOOOi", i, strobj,
+ path1 ? path1->object : Py_None, Py_None,
+ path2 ? path2->object : Py_None, (int)err);
+ Py_DECREF(strobj);
+ if (args == NULL)
+ return;
+
+ exc = PyObject_CallObject((PyObject *)&BtrfsUtilError_type, args);
+ Py_DECREF(args);
+ if (exc == NULL)
+ return;
+
+ PyErr_SetObject((PyObject *)&BtrfsUtilError_type, exc);
+ Py_DECREF(exc);
+}
+
+static int BtrfsUtilError_clear(BtrfsUtilError *self)
+{
+ Py_CLEAR(self->btrfsutilerror);
+ return Py_TYPE(self)->tp_base->tp_clear((PyObject *)self);
+}
+
+static void BtrfsUtilError_dealloc(BtrfsUtilError *self)
+{
+ PyObject_GC_UnTrack(self);
+ BtrfsUtilError_clear(self);
+ Py_TYPE(self)->tp_free((PyObject *)self);
+}
+
+static int BtrfsUtilError_traverse(BtrfsUtilError *self, visitproc visit,
+ void *arg)
+{
+ Py_VISIT(self->btrfsutilerror);
+ return Py_TYPE(self)->tp_base->tp_traverse((PyObject *)self, visit, arg);
+}
+
+static PyObject *BtrfsUtilError_new(PyTypeObject *type, PyObject *args,
+ PyObject *kwds)
+{
+ BtrfsUtilError *self;
+ PyObject *oserror_args = args;
+
+ if (PyTuple_Check(args) && PyTuple_GET_SIZE(args) == 6) {
+ oserror_args = PyTuple_GetSlice(args, 0, 5);
+ if (oserror_args == NULL)
+ return NULL;
+ }
+
+ self = (BtrfsUtilError *)type->tp_base->tp_new(type, oserror_args,
+ kwds);
+ if (oserror_args != args)
+ Py_DECREF(oserror_args);
+ if (self == NULL)
+ return NULL;
+
+ if (PyTuple_Check(args) && PyTuple_GET_SIZE(args) == 6) {
+ self->btrfsutilerror = PyTuple_GET_ITEM(args, 5);
+ Py_INCREF(self->btrfsutilerror);
+ }
+
+ return (PyObject *)self;
+}
+
+static PyObject *BtrfsUtilError_str(BtrfsUtilError *self)
+{
+#define OR_NONE(x) ((x) ? (x) : Py_None)
+ if (self->btrfsutilerror) {
+ if (self->os_error.filename) {
+ if (self->os_error.filename2) {
+ return PyUnicode_FromFormat("[BtrfsUtilError %S Errno %S] %S: %R -> %R",
+ OR_NONE(self->btrfsutilerror),
+ OR_NONE(self->os_error.myerrno),
+ OR_NONE(self->os_error.strerror),
+ self->os_error.filename,
+ self->os_error.filename2);
+ } else {
+ return PyUnicode_FromFormat("[BtrfsUtilError %S Errno %S] %S: %R",
+ OR_NONE(self->btrfsutilerror),
+ OR_NONE(self->os_error.myerrno),
+ OR_NONE(self->os_error.strerror),
+ self->os_error.filename);
+ }
+ }
+ if (self->os_error.myerrno && self->os_error.strerror) {
+ return PyUnicode_FromFormat("[BtrfsUtilError %S Errno %S] %S",
+ self->btrfsutilerror,
+ self->os_error.myerrno,
+ self->os_error.strerror);
+ }
+ }
+ return Py_TYPE(self)->tp_base->tp_str((PyObject *)self);
+#undef OR_NONE
+}
+
+static PyMemberDef BtrfsUtilError_members[] = {
+ {"btrfsutilerror", T_OBJECT,
+ offsetof(BtrfsUtilError, btrfsutilerror), 0,
+ "btrfsutil error code"},
+ {},
+};
+
+#define BtrfsUtilError_DOC \
+ "Btrfs operation error."
+
+PyTypeObject BtrfsUtilError_type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "btrfsutil.BtrfsUtilError", /* tp_name */
+ sizeof(BtrfsUtilError), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ (destructor)BtrfsUtilError_dealloc, /* tp_dealloc */
+ NULL, /* tp_print */
+ NULL, /* tp_getattr */
+ NULL, /* tp_setattr */
+ NULL, /* tp_as_async */
+ NULL, /* tp_repr */
+ NULL, /* tp_as_number */
+ NULL, /* tp_as_sequence */
+ NULL, /* tp_as_mapping */
+ NULL, /* tp_hash */
+ NULL, /* tp_call */
+ (reprfunc)BtrfsUtilError_str, /* tp_str */
+ NULL, /* tp_getattro */
+ NULL, /* tp_setattro */
+ NULL, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /* tp_flags */
+ BtrfsUtilError_DOC, /* tp_doc */
+ (traverseproc)BtrfsUtilError_traverse, /* tp_traverse */
+ (inquiry)BtrfsUtilError_clear, /* tp_clear */
+ NULL, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ NULL, /* tp_iter */
+ NULL, /* tp_iternext */
+ NULL, /* tp_methods */
+ BtrfsUtilError_members, /* tp_members */
+ NULL, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ NULL, /* tp_descr_get */
+ NULL, /* tp_descr_set */
+ offsetof(BtrfsUtilError, os_error.dict), /* tp_dictoffset */
+ NULL, /* tp_init */
+ NULL, /* tp_alloc */
+ BtrfsUtilError_new, /* tp_new */
+};
diff --git a/libbtrfsutil/python/filesystem.c b/libbtrfsutil/python/filesystem.c
new file mode 100644
index 00000000..627ed193
--- /dev/null
+++ b/libbtrfsutil/python/filesystem.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2018 Facebook
+ *
+ * This file is part of libbtrfsutil.
+ *
+ * libbtrfsutil is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libbtrfsutil 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libbtrfsutil. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "btrfsutilpy.h"
+
+PyObject *filesystem_sync(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *keywords[] = {"path", NULL};
+ struct path_arg path = {.allow_fd = true};
+ enum btrfs_util_error err;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&:sync", keywords,
+ &path_converter, &path))
+ return NULL;
+
+ if (path.path)
+ err = btrfs_util_sync(path.path);
+ else
+ err = btrfs_util_sync_fd(path.fd);
+ if (err) {
+ SetFromBtrfsUtilErrorWithPath(err, &path);
+ path_cleanup(&path);
+ return NULL;
+ }
+
+ path_cleanup(&path);
+ Py_RETURN_NONE;
+}
+
+PyObject *start_sync(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *keywords[] = {"path", NULL};
+ struct path_arg path = {.allow_fd = true};
+ uint64_t transid;
+ enum btrfs_util_error err;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&:start_sync", keywords,
+ &path_converter, &path))
+ return NULL;
+
+ if (path.path)
+ err = btrfs_util_start_sync(path.path, &transid);
+ else
+ err = btrfs_util_start_sync_fd(path.fd, &transid);
+ if (err) {
+ SetFromBtrfsUtilErrorWithPath(err, &path);
+ path_cleanup(&path);
+ return NULL;
+ }
+
+ path_cleanup(&path);
+ return PyLong_FromUnsignedLongLong(transid);
+}
+
+PyObject *wait_sync(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *keywords[] = {"path", "transid", NULL};
+ struct path_arg path = {.allow_fd = true};
+ unsigned long long transid = 0;
+ enum btrfs_util_error err;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|K:wait_sync", keywords,
+ &path_converter, &path, &transid))
+ return NULL;
+
+ if (path.path)
+ err = btrfs_util_wait_sync(path.path, transid);
+ else
+ err = btrfs_util_wait_sync_fd(path.fd, transid);
+ if (err) {
+ SetFromBtrfsUtilErrorWithPath(err, &path);
+ path_cleanup(&path);
+ return NULL;
+ }
+
+ path_cleanup(&path);
+ Py_RETURN_NONE;
+}
diff --git a/libbtrfsutil/python/module.c b/libbtrfsutil/python/module.c
new file mode 100644
index 00000000..2dbdc7be
--- /dev/null
+++ b/libbtrfsutil/python/module.c
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2018 Facebook
+ *
+ * This file is part of libbtrfsutil.
+ *
+ * libbtrfsutil is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libbtrfsutil 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libbtrfsutil. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "btrfsutilpy.h"
+
+static int fd_converter(PyObject *o, void *p)
+{
+ int *fd = p;
+ long tmp;
+ int overflow;
+
+ tmp = PyLong_AsLongAndOverflow(o, &overflow);
+ if (tmp == -1 && PyErr_Occurred())
+ return 0;
+ if (overflow > 0 || tmp > INT_MAX) {
+ PyErr_SetString(PyExc_OverflowError,
+ "fd is greater than maximum");
+ return 0;
+ }
+ if (overflow < 0 || tmp < 0) {
+ PyErr_SetString(PyExc_ValueError, "fd is negative");
+ return 0;
+ }
+ *fd = tmp;
+ return 1;
+}
+
+int path_converter(PyObject *o, void *p)
+{
+ struct path_arg *path = p;
+ int is_index, is_bytes, is_unicode;
+ PyObject *bytes = NULL;
+ Py_ssize_t length = 0;
+ char *tmp;
+
+ if (o == NULL) {
+ path_cleanup(p);
+ return 1;
+ }
+
+ path->object = path->cleanup = NULL;
+ Py_INCREF(o);
+
+ path->fd = -1;
+
+ is_index = path->allow_fd && PyIndex_Check(o);
+ is_bytes = PyBytes_Check(o);
+ is_unicode = PyUnicode_Check(o);
+
+ if (!is_index && !is_bytes && !is_unicode) {
+ _Py_IDENTIFIER(__fspath__);
+ PyObject *func;
+
+ func = _PyObject_LookupSpecial(o, &PyId___fspath__);
+ if (func == NULL)
+ goto err_format;
+ Py_DECREF(o);
+ o = PyObject_CallFunctionObjArgs(func, NULL);
+ Py_DECREF(func);
+ if (o == NULL)
+ return 0;
+ is_bytes = PyBytes_Check(o);
+ is_unicode = PyUnicode_Check(o);
+ }
+
+ if (is_unicode) {
+ if (!PyUnicode_FSConverter(o, &bytes))
+ goto err;
+ } else if (is_bytes) {
+ bytes = o;
+ Py_INCREF(bytes);
+ } else if (is_index) {
+ if (!fd_converter(o, &path->fd))
+ goto err;
+ path->path = NULL;
+ goto out;
+ } else {
+err_format:
+ PyErr_Format(PyExc_TypeError, "expected %s, not %s",
+ path->allow_fd ? "string, bytes, os.PathLike, or integer" :
+ "string, bytes, or os.PathLike",
+ Py_TYPE(o)->tp_name);
+ goto err;
+ }
+
+ length = PyBytes_GET_SIZE(bytes);
+ tmp = PyBytes_AS_STRING(bytes);
+ if ((size_t)length != strlen(tmp)) {
+ PyErr_SetString(PyExc_TypeError,
+ "path has embedded nul character");
+ goto err;
+ }
+
+ path->path = tmp;
+ if (bytes == o)
+ Py_DECREF(bytes);
+ else
+ path->cleanup = bytes;
+ path->fd = -1;
+
+out:
+ path->length = length;
+ path->object = o;
+ return Py_CLEANUP_SUPPORTED;
+
+err:
+ Py_XDECREF(o);
+ Py_XDECREF(bytes);
+ return 0;
+}
+
+PyObject *list_from_uint64_array(const uint64_t *arr, size_t n)
+{
+ PyObject *ret;
+ size_t i;
+
+ ret = PyList_New(n);
+ if (!ret)
+ return NULL;
+
+ for (i = 0; i < n; i++) {
+ PyObject *tmp;
+
+ tmp = PyLong_FromUnsignedLongLong(arr[i]);
+ if (!tmp) {
+ Py_DECREF(ret);
+ return NULL;
+ }
+ PyList_SET_ITEM(ret, i, tmp);
+ }
+
+ return ret;
+}
+
+void path_cleanup(struct path_arg *path)
+{
+ Py_CLEAR(path->object);
+ Py_CLEAR(path->cleanup);
+}
+
+static PyMethodDef btrfsutil_methods[] = {
+ {"sync", (PyCFunction)filesystem_sync,
+ METH_VARARGS | METH_KEYWORDS,
+ "sync(path)\n\n"
+ "Sync a specific Btrfs filesystem.\n\n"
+ "Arguments:\n"
+ "path -- string, bytes, path-like object, or open file descriptor"},
+ {"start_sync", (PyCFunction)start_sync,
+ METH_VARARGS | METH_KEYWORDS,
+ "start_sync(path) -> int\n\n"
+ "Start a sync on a specific Btrfs filesystem and return the\n"
+ "transaction ID.\n\n"
+ "Arguments:\n"
+ "path -- string, bytes, path-like object, or open file descriptor"},
+ {"wait_sync", (PyCFunction)wait_sync,
+ METH_VARARGS | METH_KEYWORDS,
+ "wait_sync(path, transid=0)\n\n"
+ "Wait for a transaction to sync.\n"
+ "Arguments:\n"
+ "path -- string, bytes, path-like object, or open file descriptor\n"
+ "transid -- int transaction ID to wait for, or zero for the current\n"
+ "transaction"},
+ {"is_subvolume", (PyCFunction)is_subvolume,
+ METH_VARARGS | METH_KEYWORDS,
+ "is_subvolume(path) -> bool\n\n"
+ "Get whether a file is a subvolume.\n\n"
+ "Arguments:\n"
+ "path -- string, bytes, path-like object, or open file descriptor"},
+ {"subvolume_id", (PyCFunction)subvolume_id,
+ METH_VARARGS | METH_KEYWORDS,
+ "subvolume_id(path) -> int\n\n"
+ "Get the ID of the subvolume containing a file.\n\n"
+ "Arguments:\n"
+ "path -- string, bytes, path-like object, or open file descriptor"},
+ {"subvolume_path", (PyCFunction)subvolume_path,
+ METH_VARARGS | METH_KEYWORDS,
+ "subvolume_path(path, id=0) -> int\n\n"
+ "Get the path of a subvolume relative to the filesystem root.\n\n"
+ "Arguments:\n"
+ "path -- string, bytes, path-like object, or open file descriptor\n"
+ "id -- if not zero, instead of returning the subvolume path of the\n"
+ "given path, return the path of the subvolume with this ID"},
+ {"subvolume_info", (PyCFunction)subvolume_info,
+ METH_VARARGS | METH_KEYWORDS,
+ "subvolume_info(path, id=0) -> SubvolumeInfo\n\n"
+ "Get information about a subvolume.\n\n"
+ "Arguments:\n"
+ "path -- string, bytes, path-like object, or open file descriptor\n"
+ "id -- if not zero, instead of returning information about the\n"
+ "given path, return information about the subvolume with this ID"},
+ {"get_subvolume_read_only", (PyCFunction)get_subvolume_read_only,
+ METH_VARARGS | METH_KEYWORDS,
+ "get_subvolume_read_only(path) -> bool\n\n"
+ "Get whether a subvolume is read-only.\n\n"
+ "Arguments:\n"
+ "path -- string, bytes, path-like object, or open file descriptor"},
+ {"set_subvolume_read_only", (PyCFunction)set_subvolume_read_only,
+ METH_VARARGS | METH_KEYWORDS,
+ "set_subvolume_read_only(path, read_only=True)\n\n"
+ "Set whether a subvolume is read-only.\n\n"
+ "Arguments:\n"
+ "path -- string, bytes, path-like object, or open file descriptor\n"
+ "read_only -- bool flag value"},
+ {"get_default_subvolume", (PyCFunction)get_default_subvolume,
+ METH_VARARGS | METH_KEYWORDS,
+ "get_default_subvolume(path) -> int\n\n"
+ "Get the ID of the default subvolume of a filesystem.\n\n"
+ "Arguments:\n"
+ "path -- string, bytes, path-like object, or open file descriptor"},
+ {"set_default_subvolume", (PyCFunction)set_default_subvolume,
+ METH_VARARGS | METH_KEYWORDS,
+ "set_default_subvolume(path, id=0)\n\n"
+ "Set the default subvolume of a filesystem.\n\n"
+ "Arguments:\n"
+ "path -- string, bytes, path-like object, or open file descriptor\n"
+ "id -- if not zero, set the default subvolume to the subvolume with\n"
+ "this ID instead of the given path"},
+ {"create_subvolume", (PyCFunction)create_subvolume,
+ METH_VARARGS | METH_KEYWORDS,
+ "create_subvolume(path, async=False)\n\n"
+ "Create a new subvolume.\n\n"
+ "Arguments:\n"
+ "path -- string, bytes, or path-like object\n"
+ "async -- create the subvolume without waiting for it to commit to\n"
+ "disk and return the transaction ID"},
+ {"create_snapshot", (PyCFunction)create_snapshot,
+ METH_VARARGS | METH_KEYWORDS,
+ "create_snapshot(source, path, recursive=False, read_only=False, async=False)\n\n"
+ "Create a new snapshot.\n\n"
+ "Arguments:\n"
+ "source -- string, bytes, path-like object, or open file descriptor\n"
+ "path -- string, bytes, or path-like object\n"
+ "recursive -- also snapshot child subvolumes\n"
+ "read_only -- create a read-only snapshot\n"
+ "async -- create the subvolume without waiting for it to commit to\n"
+ "disk and return the transaction ID"},
+ {"delete_subvolume", (PyCFunction)delete_subvolume,
+ METH_VARARGS | METH_KEYWORDS,
+ "delete_subvolume(path, recursive=False)\n\n"
+ "Delete a subvolume or snapshot.\n\n"
+ "Arguments:\n"
+ "path -- string, bytes, or path-like object\n"
+ "recursive -- if the given subvolume has child subvolumes, delete\n"
+ "them instead of failing"},
+ {"deleted_subvolumes", (PyCFunction)deleted_subvolumes,
+ METH_VARARGS | METH_KEYWORDS,
+ "deleted_subvolumes(path)\n\n"
+ "Get the list of subvolume IDs which have been deleted but not yet\n"
+ "cleaned up\n\n"
+ "Arguments:\n"
+ "path -- string, bytes, path-like object, or open file descriptor"},
+ {},
+};
+
+static struct PyModuleDef btrfsutilmodule = {
+ PyModuleDef_HEAD_INIT,
+ "btrfsutil",
+ "Library for managing Btrfs filesystems",
+ -1,
+ btrfsutil_methods,
+};
+
+PyMODINIT_FUNC
+PyInit_btrfsutil(void)
+{
+ PyObject *m;
+
+ BtrfsUtilError_type.tp_base = (PyTypeObject *)PyExc_OSError;
+ if (PyType_Ready(&BtrfsUtilError_type) < 0)
+ return NULL;
+
+ if (PyStructSequence_InitType2(&SubvolumeInfo_type, &SubvolumeInfo_desc) < 0)
+ return NULL;
+
+ SubvolumeIterator_type.tp_new = PyType_GenericNew;
+ if (PyType_Ready(&SubvolumeIterator_type) < 0)
+ return NULL;
+
+ QgroupInherit_type.tp_new = PyType_GenericNew;
+ if (PyType_Ready(&QgroupInherit_type) < 0)
+ return NULL;
+
+ m = PyModule_Create(&btrfsutilmodule);
+ if (!m)
+ return NULL;
+
+ Py_INCREF(&BtrfsUtilError_type);
+ PyModule_AddObject(m, "BtrfsUtilError",
+ (PyObject *)&BtrfsUtilError_type);
+
+ Py_INCREF(&SubvolumeInfo_type);
+ PyModule_AddObject(m, "SubvolumeInfo", (PyObject *)&SubvolumeInfo_type);
+
+ Py_INCREF(&SubvolumeIterator_type);
+ PyModule_AddObject(m, "SubvolumeIterator",
+ (PyObject *)&SubvolumeIterator_type);
+
+ Py_INCREF(&QgroupInherit_type);
+ PyModule_AddObject(m, "QgroupInherit",
+ (PyObject *)&QgroupInherit_type);
+
+ add_module_constants(m);
+
+ return m;
+}
diff --git a/libbtrfsutil/python/qgroup.c b/libbtrfsutil/python/qgroup.c
new file mode 100644
index 00000000..44ac5ebc
--- /dev/null
+++ b/libbtrfsutil/python/qgroup.c
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2018 Facebook
+ *
+ * This file is part of libbtrfsutil.
+ *
+ * libbtrfsutil is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libbtrfsutil 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libbtrfsutil. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "btrfsutilpy.h"
+
+static void QgroupInherit_dealloc(QgroupInherit *self)
+{
+ btrfs_util_destroy_qgroup_inherit(self->inherit);
+ Py_TYPE(self)->tp_free((PyObject *)self);
+}
+
+static int QgroupInherit_init(QgroupInherit *self, PyObject *args,
+ PyObject *kwds)
+{
+ static char *keywords[] = {NULL};
+ enum btrfs_util_error err;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, ":QgroupInherit",
+ keywords))
+ return -1;
+
+ err = btrfs_util_create_qgroup_inherit(0, &self->inherit);
+ if (err) {
+ SetFromBtrfsUtilError(err);
+ return -1;
+ }
+
+ return 0;
+}
+
+static PyObject *QgroupInherit_getattro(QgroupInherit *self, PyObject *nameobj)
+{
+ const char *name = "";
+
+ if (PyUnicode_Check(nameobj)) {
+ name = PyUnicode_AsUTF8(nameobj);
+ if (!name)
+ return NULL;
+ }
+
+ if (strcmp(name, "groups") == 0) {
+ const uint64_t *arr;
+ size_t n;
+
+ btrfs_util_qgroup_inherit_get_groups(self->inherit, &arr, &n);
+
+ return list_from_uint64_array(arr, n);
+ } else {
+ return PyObject_GenericGetAttr((PyObject *)self, nameobj);
+ }
+}
+
+static PyObject *QgroupInherit_add_group(QgroupInherit *self, PyObject *args,
+ PyObject *kwds)
+{
+ static char *keywords[] = {"qgroupid", NULL};
+ enum btrfs_util_error err;
+ uint64_t qgroupid;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "K:add_group", keywords,
+ &qgroupid))
+ return NULL;
+
+ err = btrfs_util_qgroup_inherit_add_group(&self->inherit, qgroupid);
+ if (err) {
+ SetFromBtrfsUtilError(err);
+ return NULL;
+ }
+
+ Py_RETURN_NONE;
+}
+
+static PyMethodDef QgroupInherit_methods[] = {
+ {"add_group", (PyCFunction)QgroupInherit_add_group,
+ METH_VARARGS | METH_KEYWORDS,
+ "add_group(qgroupid)\n\n"
+ "Add a qgroup to inherit from.\n\n"
+ "Arguments:\n"
+ "qgroupid -- ID of qgroup to add"},
+ {},
+};
+
+#define QgroupInherit_DOC \
+ "QgroupInherit() -> new qgroup inheritance specifier\n\n" \
+ "Create a new object which specifies what qgroups to inherit\n" \
+ "from for create_subvolume() and create_snapshot()"
+
+PyTypeObject QgroupInherit_type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "btrfsutil.QgroupInherit", /* tp_name */
+ sizeof(QgroupInherit), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ (destructor)QgroupInherit_dealloc, /* tp_dealloc */
+ NULL, /* tp_print */
+ NULL, /* tp_getattr */
+ NULL, /* tp_setattr */
+ NULL, /* tp_as_async */
+ NULL, /* tp_repr */
+ NULL, /* tp_as_number */
+ NULL, /* tp_as_sequence */
+ NULL, /* tp_as_mapping */
+ NULL, /* tp_hash */
+ NULL, /* tp_call */
+ NULL, /* tp_str */
+ (getattrofunc)QgroupInherit_getattro, /* tp_getattro */
+ NULL, /* tp_setattro */
+ NULL, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ QgroupInherit_DOC, /* tp_doc */
+ NULL, /* tp_traverse */
+ NULL, /* tp_clear */
+ NULL, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ NULL, /* tp_iter */
+ NULL, /* tp_iternext */
+ QgroupInherit_methods, /* tp_methods */
+ NULL, /* tp_members */
+ NULL, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ NULL, /* tp_descr_get */
+ NULL, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)QgroupInherit_init, /* tp_init */
+};
diff --git a/libbtrfsutil/python/setup.py b/libbtrfsutil/python/setup.py
new file mode 100755
index 00000000..1cd3bc00
--- /dev/null
+++ b/libbtrfsutil/python/setup.py
@@ -0,0 +1,111 @@
+#!/usr/bin/env python3
+
+# Copyright (C) 2018 Facebook
+#
+# This file is part of libbtrfsutil.
+#
+# libbtrfsutil is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# libbtrfsutil 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with libbtrfsutil. If not, see <http://www.gnu.org/licenses/>.
+
+import re
+import os
+import os.path
+from setuptools import setup, Extension
+from setuptools.command.build_ext import build_ext
+import subprocess
+
+
+def get_version():
+ with open('../btrfsutil.h', 'r') as f:
+ btrfsutil_h = f.read()
+ major = re.search(r'^#define BTRFS_UTIL_VERSION_MAJOR ([0-9]+)$',
+ btrfsutil_h, flags=re.MULTILINE).group(1)
+ minor = re.search(r'^#define BTRFS_UTIL_VERSION_MINOR ([0-9]+)$',
+ btrfsutil_h, flags=re.MULTILINE).group(1)
+ patch = re.search(r'^#define BTRFS_UTIL_VERSION_PATCH ([0-9]+)$',
+ btrfsutil_h, flags=re.MULTILINE).group(1)
+ return major + '.' + minor + '.' + patch
+
+
+def out_of_date(dependencies, target):
+ dependency_mtimes = [os.path.getmtime(dependency) for dependency in dependencies]
+ try:
+ target_mtime = os.path.getmtime(target)
+ except OSError:
+ return True
+ return any(dependency_mtime >= target_mtime for dependency_mtime in dependency_mtimes)
+
+
+def gen_constants():
+ with open('../btrfsutil.h', 'r') as f:
+ btrfsutil_h = f.read()
+
+ constants = re.findall(
+ r'^\s*(BTRFS_UTIL_ERROR_[a-zA-Z0-9_]+)',
+ btrfsutil_h, flags=re.MULTILINE)
+
+ with open('constants.c', 'w') as f:
+ f.write("""\
+#include <btrfsutil.h>
+#include "btrfsutilpy.h"
+
+void add_module_constants(PyObject *m)
+{
+""")
+ for constant in constants:
+ assert constant.startswith('BTRFS_UTIL_')
+ name = constant[len('BTRFS_UTIL_'):]
+ f.write('\tPyModule_AddIntConstant(m, "{}", {});\n'.format(name, constant))
+ f.write("""\
+}
+""")
+
+
+class my_build_ext(build_ext):
+ def run(self):
+ if out_of_date(['../btrfsutil.h'], 'constants.c'):
+ try:
+ gen_constants()
+ except Exception as e:
+ try:
+ os.remove('constants.c')
+ except OSError:
+ pass
+ raise e
+ super().run()
+
+
+module = Extension(
+ name='btrfsutil',
+ sources=[
+ 'constants.c',
+ 'error.c',
+ 'filesystem.c',
+ 'module.c',
+ 'qgroup.c',
+ 'subvolume.c',
+ ],
+ include_dirs=['..'],
+ library_dirs=['../..'],
+ libraries=['btrfsutil'],
+)
+
+setup(
+ name='btrfsutil',
+ version=get_version(),
+ description='Library for managing Btrfs filesystems',
+ url='https://github.com/kdave/btrfs-progs',
+ license='LGPLv3',
+ cmdclass={'build_ext': my_build_ext},
+ ext_modules=[module],
+)
diff --git a/libbtrfsutil/python/subvolume.c b/libbtrfsutil/python/subvolume.c
new file mode 100644
index 00000000..069e606b
--- /dev/null
+++ b/libbtrfsutil/python/subvolume.c
@@ -0,0 +1,667 @@
+/*
+ * Copyright (C) 2018 Facebook
+ *
+ * This file is part of libbtrfsutil.
+ *
+ * libbtrfsutil is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libbtrfsutil 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libbtrfsutil. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "btrfsutilpy.h"
+
+PyObject *is_subvolume(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *keywords[] = {"path", NULL};
+ struct path_arg path = {.allow_fd = true};
+ enum btrfs_util_error err;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&:is_subvolume",
+ keywords, &path_converter, &path))
+ return NULL;
+
+ if (path.path)
+ err = btrfs_util_is_subvolume(path.path);
+ else
+ err = btrfs_util_is_subvolume_fd(path.fd);
+ if (err == BTRFS_UTIL_OK) {
+ path_cleanup(&path);
+ Py_RETURN_TRUE;
+ } else if (err == BTRFS_UTIL_ERROR_NOT_BTRFS ||
+ err == BTRFS_UTIL_ERROR_NOT_SUBVOLUME) {
+ path_cleanup(&path);
+ Py_RETURN_FALSE;
+ } else {
+ SetFromBtrfsUtilErrorWithPath(err, &path);
+ path_cleanup(&path);
+ return NULL;
+ }
+}
+
+PyObject *subvolume_id(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *keywords[] = {"path", NULL};
+ struct path_arg path = {.allow_fd = true};
+ enum btrfs_util_error err;
+ uint64_t id;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&:subvolume_id",
+ keywords, &path_converter, &path))
+ return NULL;
+
+ if (path.path)
+ err = btrfs_util_subvolume_id(path.path, &id);
+ else
+ err = btrfs_util_subvolume_id_fd(path.fd, &id);
+ if (err) {
+ SetFromBtrfsUtilErrorWithPath(err, &path);
+ path_cleanup(&path);
+ return NULL;
+ }
+
+ path_cleanup(&path);
+ return PyLong_FromUnsignedLongLong(id);
+}
+
+PyObject *subvolume_path(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *keywords[] = {"path", "id", NULL};
+ struct path_arg path = {.allow_fd = true};
+ enum btrfs_util_error err;
+ uint64_t id = 0;
+ char *subvol_path;
+ PyObject *ret;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|K:subvolume_path",
+ keywords, &path_converter, &path, &id))
+ return NULL;
+
+ if (path.path)
+ err = btrfs_util_subvolume_path(path.path, id, &subvol_path);
+ else
+ err = btrfs_util_subvolume_path_fd(path.fd, id, &subvol_path);
+ if (err) {
+ SetFromBtrfsUtilErrorWithPath(err, &path);
+ path_cleanup(&path);
+ return NULL;
+ }
+
+ path_cleanup(&path);
+
+ ret = PyUnicode_DecodeFSDefault(subvol_path);
+ free(subvol_path);
+ return ret;
+}
+
+static PyObject *subvolume_info_to_object(const struct btrfs_util_subvolume_info *subvol)
+{
+ PyObject *ret, *tmp;
+
+ ret = PyStructSequence_New(&SubvolumeInfo_type);
+ if (ret == NULL)
+ return NULL;
+
+#define SET_UINT64(i, field) \
+ tmp = PyLong_FromUnsignedLongLong(subvol->field); \
+ if (tmp == NULL) { \
+ Py_DECREF(ret); \
+ return ret; \
+ } \
+ PyStructSequence_SET_ITEM(ret, i, tmp);
+
+#define SET_UUID(i, field) \
+ tmp = PyBytes_FromStringAndSize((char *)subvol->field, 16); \
+ if (tmp == NULL) { \
+ Py_DECREF(ret); \
+ return ret; \
+ } \
+ PyStructSequence_SET_ITEM(ret, i, tmp);
+
+#define SET_TIME(i, field) \
+ tmp = PyFloat_FromDouble(subvol->field.tv_sec + \
+ subvol->field.tv_nsec / 1000000000); \
+ if (tmp == NULL) { \
+ Py_DECREF(ret); \
+ return ret; \
+ } \
+ PyStructSequence_SET_ITEM(ret, i, tmp);
+
+ SET_UINT64(0, id);
+ SET_UINT64(1, parent_id);
+ SET_UINT64(2, dir_id);
+ SET_UINT64(3, flags);
+ SET_UUID(4, uuid);
+ SET_UUID(5, parent_uuid);
+ SET_UUID(6, received_uuid);
+ SET_UINT64(7, generation);
+ SET_UINT64(8, ctransid);
+ SET_UINT64(9, otransid);
+ SET_UINT64(10, stransid);
+ SET_UINT64(11, rtransid);
+ SET_TIME(12, ctime);
+ SET_TIME(13, otime);
+ SET_TIME(14, stime);
+ SET_TIME(15, rtime);
+
+#undef SET_TIME
+#undef SET_UUID
+#undef SET_UINT64
+
+ return ret;
+}
+
+PyObject *subvolume_info(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *keywords[] = {"path", "id", NULL};
+ struct path_arg path = {.allow_fd = true};
+ struct btrfs_util_subvolume_info subvol;
+ enum btrfs_util_error err;
+ uint64_t id = 0;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|K:subvolume_info",
+ keywords, &path_converter, &path, &id))
+ return NULL;
+
+ if (path.path)
+ err = btrfs_util_subvolume_info(path.path, id, &subvol);
+ else
+ err = btrfs_util_subvolume_info_fd(path.fd, id, &subvol);
+ if (err) {
+ SetFromBtrfsUtilErrorWithPath(err, &path);
+ path_cleanup(&path);
+ return NULL;
+ }
+
+ path_cleanup(&path);
+
+ return subvolume_info_to_object(&subvol);
+}
+
+static PyStructSequence_Field SubvolumeInfo_fields[] = {
+ {"id", "int ID of this subvolume"},
+ {"parent_id", "int ID of the subvolume containing this subvolume"},
+ {"dir_id", "int inode number of the directory containing this subvolume"},
+ {"flags", "int root item flags"},
+ {"uuid", "bytes UUID of this subvolume"},
+ {"parent_uuid", "bytes UUID of the subvolume this is a snapshot of"},
+ {"received_uuid", "bytes UUID of the subvolume this was received from"},
+ {"generation", "int transaction ID of the subvolume root"},
+ {"ctransid", "int transaction ID when an inode was last changed"},
+ {"otransid", "int transaction ID when this subvolume was created"},
+ {"stransid", "int transaction ID of the sent subvolume this subvolume was received from"},
+ {"rtransid", "int transaction ID when this subvolume was received"},
+ {"ctime", "float time when an inode was last changed"},
+ {"otime", "float time when this subvolume was created"},
+ {"stime", "float time, usually zero"},
+ {"rtime", "float time when this subvolume was received"},
+ {},
+};
+
+PyStructSequence_Desc SubvolumeInfo_desc = {
+ "btrfsutil.SubvolumeInfo",
+ "Information about a Btrfs subvolume.",
+ SubvolumeInfo_fields,
+ 14,
+};
+
+PyTypeObject SubvolumeInfo_type;
+
+PyObject *get_subvolume_read_only(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *keywords[] = {"path", NULL};
+ struct path_arg path = {.allow_fd = true};
+ enum btrfs_util_error err;
+ bool read_only;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds,
+ "O&:get_subvolume_read_only",
+ keywords, &path_converter, &path))
+ return NULL;
+
+ if (path.path) {
+ err = btrfs_util_get_subvolume_read_only(path.path, &read_only);
+ } else {
+ err = btrfs_util_get_subvolume_read_only_fd(path.fd,
+ &read_only);
+ }
+ if (err) {
+ SetFromBtrfsUtilErrorWithPath(err, &path);
+ path_cleanup(&path);
+ return NULL;
+ }
+
+ path_cleanup(&path);
+ return PyBool_FromLong(read_only);
+}
+
+PyObject *set_subvolume_read_only(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *keywords[] = {"path", "read_only", NULL};
+ struct path_arg path = {.allow_fd = true};
+ enum btrfs_util_error err;
+ int read_only = 1;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds,
+ "O&|p:set_subvolume_read_only",
+ keywords, &path_converter, &path,
+ &read_only))
+ return NULL;
+
+ if (path.path)
+ err = btrfs_util_set_subvolume_read_only(path.path, read_only);
+ else
+ err = btrfs_util_set_subvolume_read_only_fd(path.fd, read_only);
+ if (err) {
+ SetFromBtrfsUtilErrorWithPath(err, &path);
+ path_cleanup(&path);
+ return NULL;
+ }
+
+ path_cleanup(&path);
+ Py_RETURN_NONE;
+}
+
+PyObject *get_default_subvolume(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *keywords[] = {"path", NULL};
+ struct path_arg path = {.allow_fd = true};
+ enum btrfs_util_error err;
+ uint64_t id;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&:get_default_subvolume",
+ keywords, &path_converter, &path))
+ return NULL;
+
+ if (path.path)
+ err = btrfs_util_get_default_subvolume(path.path, &id);
+ else
+ err = btrfs_util_get_default_subvolume_fd(path.fd, &id);
+ if (err) {
+ SetFromBtrfsUtilErrorWithPath(err, &path);
+ path_cleanup(&path);
+ return NULL;
+ }
+
+ path_cleanup(&path);
+ return PyLong_FromUnsignedLongLong(id);
+}
+
+PyObject *set_default_subvolume(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *keywords[] = {"path", "id", NULL};
+ struct path_arg path = {.allow_fd = true};
+ enum btrfs_util_error err;
+ uint64_t id = 0;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|K:set_default_subvolume",
+ keywords, &path_converter, &path, &id))
+ return NULL;
+
+ if (path.path)
+ err = btrfs_util_set_default_subvolume(path.path, id);
+ else
+ err = btrfs_util_set_default_subvolume_fd(path.fd, id);
+ if (err) {
+ SetFromBtrfsUtilErrorWithPath(err, &path);
+ path_cleanup(&path);
+ return NULL;
+ }
+
+ path_cleanup(&path);
+ Py_RETURN_NONE;
+}
+
+PyObject *create_subvolume(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *keywords[] = {"path", "async", "qgroup_inherit", NULL};
+ struct path_arg path = {.allow_fd = false};
+ enum btrfs_util_error err;
+ int async = 0;
+ QgroupInherit *inherit = NULL;
+ uint64_t transid;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|pO!:create_subvolume",
+ keywords, &path_converter, &path,
+ &async, &QgroupInherit_type, &inherit))
+ return NULL;
+
+ err = btrfs_util_create_subvolume(path.path, 0, async ? &transid : NULL,
+ inherit ? inherit->inherit : NULL);
+ if (err) {
+ SetFromBtrfsUtilErrorWithPath(err, &path);
+ path_cleanup(&path);
+ return NULL;
+ }
+
+ path_cleanup(&path);
+ if (async)
+ return PyLong_FromUnsignedLongLong(transid);
+ else
+ Py_RETURN_NONE;
+}
+
+PyObject *create_snapshot(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *keywords[] = {
+ "source", "path", "recursive", "read_only", "async",
+ "qgroup_inherit", NULL,
+ };
+ struct path_arg src = {.allow_fd = true}, dst = {.allow_fd = false};
+ enum btrfs_util_error err;
+ int recursive = 0, read_only = 0, async = 0;
+ int flags = 0;
+ QgroupInherit *inherit = NULL;
+ uint64_t transid;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&O&|pppO!:create_snapshot",
+ keywords, &path_converter, &src,
+ &path_converter, &dst, &recursive,
+ &read_only, &async,
+ &QgroupInherit_type, &inherit))
+ return NULL;
+
+ if (recursive)
+ flags |= BTRFS_UTIL_CREATE_SNAPSHOT_RECURSIVE;
+ if (read_only)
+ flags |= BTRFS_UTIL_CREATE_SNAPSHOT_READ_ONLY;
+
+ if (src.path) {
+ err = btrfs_util_create_snapshot(src.path, dst.path, flags,
+ async ? &transid : NULL,
+ inherit ? inherit->inherit : NULL);
+ } else {
+ err = btrfs_util_create_snapshot_fd(src.fd, dst.path, flags,
+ async ? &transid : NULL,
+ inherit ? inherit->inherit : NULL);
+ }
+ if (err) {
+ SetFromBtrfsUtilErrorWithPaths(err, &src, &dst);
+ path_cleanup(&src);
+ path_cleanup(&dst);
+ return NULL;
+ }
+
+ path_cleanup(&src);
+ path_cleanup(&dst);
+ if (async)
+ return PyLong_FromUnsignedLongLong(transid);
+ else
+ Py_RETURN_NONE;
+}
+
+PyObject *delete_subvolume(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *keywords[] = {"path", "recursive", NULL};
+ struct path_arg path = {.allow_fd = false};
+ enum btrfs_util_error err;
+ int recursive = 0;
+ int flags = 0;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|p:delete_subvolume",
+ keywords, &path_converter, &path,
+ &recursive))
+ return NULL;
+
+ if (recursive)
+ flags |= BTRFS_UTIL_DELETE_SUBVOLUME_RECURSIVE;
+
+ err = btrfs_util_delete_subvolume(path.path, flags);
+ if (err) {
+ SetFromBtrfsUtilErrorWithPath(err, &path);
+ path_cleanup(&path);
+ return NULL;
+ }
+
+ path_cleanup(&path);
+ Py_RETURN_NONE;
+}
+
+PyObject *deleted_subvolumes(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *keywords[] = {"path", NULL};
+ struct path_arg path = {.allow_fd = true};
+ PyObject *ret;
+ uint64_t *ids;
+ size_t n;
+ enum btrfs_util_error err;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&:deleted_subvolumes",
+ keywords, &path_converter, &path))
+ return NULL;
+
+ if (path.path)
+ err = btrfs_util_deleted_subvolumes(path.path, &ids, &n);
+ else
+ err = btrfs_util_deleted_subvolumes_fd(path.fd, &ids, &n);
+ if (err) {
+ SetFromBtrfsUtilErrorWithPath(err, &path);
+ path_cleanup(&path);
+ return NULL;
+ }
+
+ path_cleanup(&path);
+
+ ret = list_from_uint64_array(ids, n);
+ free(ids);
+ return ret;
+}
+
+typedef struct {
+ PyObject_HEAD
+ struct btrfs_util_subvolume_iterator *iter;
+ bool info;
+} SubvolumeIterator;
+
+static void SubvolumeIterator_dealloc(SubvolumeIterator *self)
+{
+ btrfs_util_destroy_subvolume_iterator(self->iter);
+ Py_TYPE(self)->tp_free((PyObject *)self);
+}
+
+static PyObject *SubvolumeIterator_next(SubvolumeIterator *self)
+{
+ enum btrfs_util_error err;
+ PyObject *ret, *tmp;
+ char *path;
+
+ if (!self->iter) {
+ PyErr_SetString(PyExc_ValueError,
+ "operation on closed iterator");
+ return NULL;
+ }
+
+ if (self->info) {
+ struct btrfs_util_subvolume_info subvol;
+
+ err = btrfs_util_subvolume_iterator_next_info(self->iter, &path,
+ &subvol);
+ if (err == BTRFS_UTIL_ERROR_STOP_ITERATION) {
+ PyErr_SetNone(PyExc_StopIteration);
+ return NULL;
+ } else if (err) {
+ SetFromBtrfsUtilError(err);
+ return NULL;
+ }
+
+ tmp = subvolume_info_to_object(&subvol);
+ } else {
+ uint64_t id;
+
+ err = btrfs_util_subvolume_iterator_next(self->iter, &path, &id);
+ if (err == BTRFS_UTIL_ERROR_STOP_ITERATION) {
+ PyErr_SetNone(PyExc_StopIteration);
+ return NULL;
+ } else if (err) {
+ SetFromBtrfsUtilError(err);
+ return NULL;
+ }
+
+ tmp = PyLong_FromUnsignedLongLong(id);
+
+ }
+ if (tmp) {
+ ret = Py_BuildValue("O&O", PyUnicode_DecodeFSDefault, path,
+ tmp);
+ Py_DECREF(tmp);
+ free(path);
+ } else {
+ ret = NULL;
+ }
+ return ret;
+}
+
+static int SubvolumeIterator_init(SubvolumeIterator *self, PyObject *args,
+ PyObject *kwds)
+{
+ static char *keywords[] = {"path", "top", "info", "post_order", NULL};
+ struct path_arg path = {.allow_fd = true};
+ enum btrfs_util_error err;
+ unsigned long long top = 5;
+ int info = 0;
+ int post_order = 0;
+ int flags = 0;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|Kpp:SubvolumeIterator",
+ keywords, &path_converter, &path, &top,
+ &info, &post_order))
+ return -1;
+
+ if (post_order)
+ flags |= BTRFS_UTIL_SUBVOLUME_ITERATOR_POST_ORDER;
+
+ if (path.path) {
+ err = btrfs_util_create_subvolume_iterator(path.path, top,
+ flags, &self->iter);
+ } else {
+ err = btrfs_util_create_subvolume_iterator_fd(path.fd, top,
+ flags,
+ &self->iter);
+ }
+ if (err) {
+ SetFromBtrfsUtilErrorWithPath(err, &path);
+ path_cleanup(&path);
+ return -1;
+ }
+
+ self->info = info;
+
+ return 0;
+}
+
+static PyObject *SubvolumeIterator_close(SubvolumeIterator *self)
+{
+ if (self->iter) {
+ btrfs_util_destroy_subvolume_iterator(self->iter);
+ self->iter = NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+static PyObject *SubvolumeIterator_fileno(SubvolumeIterator *self)
+{
+ if (!self->iter) {
+ PyErr_SetString(PyExc_ValueError,
+ "operation on closed iterator");
+ return NULL;
+ }
+ return PyLong_FromLong(btrfs_util_subvolume_iterator_fd(self->iter));
+}
+
+static PyObject *SubvolumeIterator_enter(SubvolumeIterator *self)
+{
+ Py_INCREF((PyObject *)self);
+ return (PyObject *)self;
+}
+
+static PyObject *SubvolumeIterator_exit(SubvolumeIterator *self, PyObject *args,
+ PyObject *kwds)
+{
+ static char *keywords[] = {"exc_type", "exc_value", "traceback", NULL};
+ PyObject *exc_type, *exc_value, *traceback;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "OOO:__exit__", keywords,
+ &exc_type, &exc_value, &traceback))
+ return NULL;
+
+ return SubvolumeIterator_close(self);
+}
+
+#define SubvolumeIterator_DOC \
+ "SubvolumeIterator(path, top=0, info=False, post_order=False) -> new subvolume iterator\n\n" \
+ "Create a new iterator that produces tuples of (path, ID) representing\n" \
+ "subvolumes on a filesystem.\n\n" \
+ "Arguments:\n" \
+ "path -- string, bytes, path-like object, or open file descriptor in\n" \
+ "filesystem to list\n" \
+ "top -- if not zero, instead of only listing subvolumes beneath the\n" \
+ "given path, list subvolumes beneath the subvolume with this ID; passing\n" \
+ "BTRFS_FS_TREE_OBJECTID (5) here lists all subvolumes. The subvolumes\n" \
+ "are listed relative to the subvolume with this ID.\n" \
+ "info -- bool indicating the iterator should yield SubvolumeInfo instead of\n" \
+ "the subvolume ID\n" \
+ "post_order -- bool indicating whether to yield parent subvolumes before\n" \
+ "child subvolumes (e.g., 'foo/bar' before 'foo')"
+
+static PyMethodDef SubvolumeIterator_methods[] = {
+ {"close", (PyCFunction)SubvolumeIterator_close,
+ METH_NOARGS,
+ "close()\n\n"
+ "Close this iterator."},
+ {"fileno", (PyCFunction)SubvolumeIterator_fileno,
+ METH_NOARGS,
+ "fileno() -> int\n\n"
+ "Get the file descriptor associated with this iterator."},
+ {"__enter__", (PyCFunction)SubvolumeIterator_enter,
+ METH_NOARGS, ""},
+ {"__exit__", (PyCFunction)SubvolumeIterator_exit,
+ METH_VARARGS | METH_KEYWORDS, ""},
+ {},
+};
+
+PyTypeObject SubvolumeIterator_type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "btrfsutil.SubvolumeIterator", /* tp_name */
+ sizeof(SubvolumeIterator), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ (destructor)SubvolumeIterator_dealloc, /* tp_dealloc */
+ NULL, /* tp_print */
+ NULL, /* tp_getattr */
+ NULL, /* tp_setattr */
+ NULL, /* tp_as_async */
+ NULL, /* tp_repr */
+ NULL, /* tp_as_number */
+ NULL, /* tp_as_sequence */
+ NULL, /* tp_as_mapping */
+ NULL, /* tp_hash */
+ NULL, /* tp_call */
+ NULL, /* tp_str */
+ NULL, /* tp_getattro */
+ NULL, /* tp_setattro */
+ NULL, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ SubvolumeIterator_DOC, /* tp_doc */
+ NULL, /* tp_traverse */
+ NULL, /* tp_clear */
+ NULL, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ PyObject_SelfIter, /* tp_iter */
+ (iternextfunc)SubvolumeIterator_next, /* tp_iternext */
+ SubvolumeIterator_methods, /* tp_methods */
+ NULL, /* tp_members */
+ NULL, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ NULL, /* tp_descr_get */
+ NULL, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)SubvolumeIterator_init, /* tp_init */
+};
diff --git a/libbtrfsutil/python/tests/__init__.py b/libbtrfsutil/python/tests/__init__.py
new file mode 100644
index 00000000..35550e0a
--- /dev/null
+++ b/libbtrfsutil/python/tests/__init__.py
@@ -0,0 +1,70 @@
+# Copyright (C) 2018 Facebook
+#
+# This file is part of libbtrfsutil.
+#
+# libbtrfsutil is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# libbtrfsutil 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with libbtrfsutil. If not, see <http://www.gnu.org/licenses/>.
+
+import os
+from pathlib import PurePath
+import subprocess
+import tempfile
+import unittest
+
+
+HAVE_PATH_LIKE = hasattr(PurePath, '__fspath__')
+
+
+@unittest.skipIf(os.geteuid() != 0, 'must be run as root')
+class BtrfsTestCase(unittest.TestCase):
+ def setUp(self):
+ self.mountpoint = tempfile.mkdtemp()
+ try:
+ with tempfile.NamedTemporaryFile(delete=False) as f:
+ os.truncate(f.fileno(), 1024 * 1024 * 1024)
+ self.image = f.name
+ except Exception as e:
+ os.rmdir(self.mountpoint)
+ raise e
+
+ if os.path.exists('../../mkfs.btrfs'):
+ mkfs = '../../mkfs.btrfs'
+ else:
+ mkfs = 'mkfs.btrfs'
+ try:
+ subprocess.check_call([mkfs, '-q', self.image])
+ subprocess.check_call(['mount', '-o', 'loop', '--', self.image, self.mountpoint])
+ except Exception as e:
+ os.remove(self.image)
+ os.rmdir(self.mountpoint)
+ raise e
+
+ def tearDown(self):
+ try:
+ subprocess.check_call(['umount', self.mountpoint])
+ finally:
+ os.remove(self.image)
+ os.rmdir(self.mountpoint)
+
+ @staticmethod
+ def path_or_fd(path, open_flags=os.O_RDONLY):
+ yield path
+ yield path.encode()
+ if HAVE_PATH_LIKE:
+ yield PurePath(path)
+ fd = os.open(path, open_flags)
+ try:
+ yield fd
+ finally:
+ os.close(fd)
+
diff --git a/libbtrfsutil/python/tests/test_filesystem.py b/libbtrfsutil/python/tests/test_filesystem.py
new file mode 100644
index 00000000..006a6b1e
--- /dev/null
+++ b/libbtrfsutil/python/tests/test_filesystem.py
@@ -0,0 +1,73 @@
+# Copyright (C) 2018 Facebook
+#
+# This file is part of libbtrfsutil.
+#
+# libbtrfsutil is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# libbtrfsutil 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with libbtrfsutil. If not, see <http://www.gnu.org/licenses/>.
+
+import os
+import time
+
+import btrfsutil
+from tests import BtrfsTestCase, HAVE_PATH_LIKE
+
+
+def touch(path):
+ now = time.time()
+ os.utime(path, (now, now))
+
+
+class TestSubvolume(BtrfsTestCase):
+ def super_generation(self):
+ with open(self.image, 'rb') as f:
+ # csum is 32 bytes, fsid is 16 bytes, bytenr is 8 bytes, flags is 8
+ # bytes
+ f.seek(65536 + 32 + 16 + 8 + 8)
+ self.assertEqual(f.read(8), b'_BHRfS_M')
+ return int.from_bytes(f.read(8), 'little')
+
+ def test_sync(self):
+ old_generation = self.super_generation()
+ for arg in self.path_or_fd(self.mountpoint):
+ with self.subTest(type=type(arg)):
+ touch(arg)
+ btrfsutil.sync(arg)
+ new_generation = self.super_generation()
+ self.assertGreater(new_generation, old_generation)
+ old_generation = new_generation
+
+ def test_start_sync(self):
+ old_generation = self.super_generation()
+ for arg in self.path_or_fd(self.mountpoint):
+ with self.subTest(type=type(arg)):
+ touch(arg)
+ transid = btrfsutil.start_sync(arg)
+ self.assertGreater(transid, old_generation)
+
+ def test_wait_sync(self):
+ old_generation = self.super_generation()
+ for arg in self.path_or_fd(self.mountpoint):
+ with self.subTest(type=type(arg)):
+ touch(arg)
+ transid = btrfsutil.start_sync(arg)
+ btrfsutil.wait_sync(arg, transid)
+ new_generation = self.super_generation()
+ self.assertGreater(new_generation, old_generation)
+ old_generation = new_generation
+
+ touch(arg)
+ btrfsutil.start_sync(arg)
+ btrfsutil.wait_sync(arg)
+ new_generation = self.super_generation()
+ self.assertGreater(new_generation, old_generation)
+ old_generation = new_generation
diff --git a/libbtrfsutil/python/tests/test_qgroup.py b/libbtrfsutil/python/tests/test_qgroup.py
new file mode 100644
index 00000000..74fc46b6
--- /dev/null
+++ b/libbtrfsutil/python/tests/test_qgroup.py
@@ -0,0 +1,57 @@
+# Copyright (C) 2018 Facebook
+#
+# This file is part of libbtrfsutil.
+#
+# libbtrfsutil is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# libbtrfsutil 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with libbtrfsutil. If not, see <http://www.gnu.org/licenses/>.
+
+import os
+import unittest
+
+import btrfsutil
+from tests import BtrfsTestCase
+
+
+class TestQgroup(BtrfsTestCase):
+ def test_subvolume_inherit(self):
+ subvol = os.path.join(self.mountpoint, 'subvol')
+
+ inherit = btrfsutil.QgroupInherit()
+ inherit.add_group(5)
+
+ btrfsutil.create_subvolume(subvol, qgroup_inherit=inherit)
+
+ def test_snapshot_inherit(self):
+ subvol = os.path.join(self.mountpoint, 'subvol')
+ snapshot = os.path.join(self.mountpoint, 'snapshot')
+
+ inherit = btrfsutil.QgroupInherit()
+ inherit.add_group(5)
+
+ btrfsutil.create_subvolume(subvol)
+ btrfsutil.create_snapshot(subvol, snapshot, qgroup_inherit=inherit)
+
+
+class TestQgroupInherit(unittest.TestCase):
+ def test_new(self):
+ inherit = btrfsutil.QgroupInherit()
+ self.assertEqual(inherit.groups, [])
+
+ def test_add_group(self):
+ inherit = btrfsutil.QgroupInherit()
+ inherit.add_group(1)
+ self.assertEqual(inherit.groups, [1])
+ inherit.add_group(2)
+ self.assertEqual(inherit.groups, [1, 2])
+ inherit.add_group(3)
+ self.assertEqual(inherit.groups, [1, 2, 3])
diff --git a/libbtrfsutil/python/tests/test_subvolume.py b/libbtrfsutil/python/tests/test_subvolume.py
new file mode 100644
index 00000000..93396cba
--- /dev/null
+++ b/libbtrfsutil/python/tests/test_subvolume.py
@@ -0,0 +1,385 @@
+# Copyright (C) 2018 Facebook
+#
+# This file is part of libbtrfsutil.
+#
+# libbtrfsutil is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# libbtrfsutil 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with libbtrfsutil. If not, see <http://www.gnu.org/licenses/>.
+
+import fcntl
+import errno
+import os
+import os.path
+from pathlib import PurePath
+import traceback
+
+import btrfsutil
+from tests import BtrfsTestCase, HAVE_PATH_LIKE
+
+
+class TestSubvolume(BtrfsTestCase):
+ def test_is_subvolume(self):
+ dir = os.path.join(self.mountpoint, 'foo')
+ os.mkdir(dir)
+
+ for arg in self.path_or_fd(self.mountpoint):
+ with self.subTest(type=type(arg)):
+ self.assertTrue(btrfsutil.is_subvolume(arg))
+ for arg in self.path_or_fd(dir):
+ with self.subTest(type=type(arg)):
+ self.assertFalse(btrfsutil.is_subvolume(arg))
+
+ with self.assertRaises(btrfsutil.BtrfsUtilError) as e:
+ btrfsutil.is_subvolume(os.path.join(self.mountpoint, 'bar'))
+ # This is a bit of an implementation detail, but really this is testing
+ # that the exception is initialized correctly.
+ self.assertEqual(e.exception.btrfsutilerror, btrfsutil.ERROR_STATFS_FAILED)
+ self.assertEqual(e.exception.errno, errno.ENOENT)
+
+ def test_subvolume_id(self):
+ dir = os.path.join(self.mountpoint, 'foo')
+ os.mkdir(dir)
+
+ for arg in self.path_or_fd(self.mountpoint):
+ with self.subTest(type=type(arg)):
+ self.assertEqual(btrfsutil.subvolume_id(arg), 5)
+ for arg in self.path_or_fd(dir):
+ with self.subTest(type=type(arg)):
+ self.assertEqual(btrfsutil.subvolume_id(arg), 5)
+
+ def test_subvolume_path(self):
+ btrfsutil.create_subvolume(os.path.join(self.mountpoint, 'subvol1'))
+ os.mkdir(os.path.join(self.mountpoint, 'dir1'))
+ os.mkdir(os.path.join(self.mountpoint, 'dir1/dir2'))
+ btrfsutil.create_subvolume(os.path.join(self.mountpoint, 'dir1/dir2/subvol2'))
+ btrfsutil.create_subvolume(os.path.join(self.mountpoint, 'dir1/dir2/subvol2/subvol3'))
+ os.mkdir(os.path.join(self.mountpoint, 'subvol1/dir3'))
+ btrfsutil.create_subvolume(os.path.join(self.mountpoint, 'subvol1/dir3/subvol4'))
+
+ for arg in self.path_or_fd(self.mountpoint):
+ with self.subTest(type=type(arg)):
+ self.assertEqual(btrfsutil.subvolume_path(arg), '')
+ self.assertEqual(btrfsutil.subvolume_path(arg, 5), '')
+ self.assertEqual(btrfsutil.subvolume_path(arg, 256), 'subvol1')
+ self.assertEqual(btrfsutil.subvolume_path(arg, 257), 'dir1/dir2/subvol2')
+ self.assertEqual(btrfsutil.subvolume_path(arg, 258), 'dir1/dir2/subvol2/subvol3')
+ self.assertEqual(btrfsutil.subvolume_path(arg, 259), 'subvol1/dir3/subvol4')
+
+ pwd = os.getcwd()
+ try:
+ os.chdir(self.mountpoint)
+ path = ''
+ for i in range(26):
+ name = chr(ord('a') + i) * 255
+ path = os.path.join(path, name)
+ btrfsutil.create_subvolume(name)
+ os.chdir(name)
+ self.assertEqual(btrfsutil.subvolume_path('.'), path)
+ finally:
+ os.chdir(pwd)
+
+ def test_subvolume_info(self):
+ for arg in self.path_or_fd(self.mountpoint):
+ with self.subTest(type=type(arg)):
+ info = btrfsutil.subvolume_info(arg)
+ self.assertEqual(info.id, 5)
+ self.assertEqual(info.parent_id, 0)
+ self.assertEqual(info.dir_id, 0)
+ self.assertEqual(info.flags, 0)
+ self.assertIsInstance(info.uuid, bytes)
+ self.assertEqual(len(info.uuid), 16)
+ self.assertEqual(info.parent_uuid, bytes(16))
+ self.assertEqual(info.received_uuid, bytes(16))
+ self.assertNotEqual(info.generation, 0)
+ self.assertEqual(info.ctransid, 0)
+ self.assertEqual(info.otransid, 0)
+ self.assertEqual(info.stransid, 0)
+ self.assertEqual(info.rtransid, 0)
+ self.assertIsInstance(info.ctime, float)
+ self.assertIsInstance(info.otime, float)
+ self.assertEqual(info.stime, 0)
+ self.assertEqual(info.rtime, 0)
+
+ subvol = os.path.join(self.mountpoint, 'subvol')
+ btrfsutil.create_subvolume(subvol)
+
+ info = btrfsutil.subvolume_info(subvol)
+ self.assertEqual(info.id, 256)
+ self.assertEqual(info.parent_id, 5)
+ self.assertEqual(info.dir_id, 256)
+ self.assertEqual(info.flags, 0)
+ self.assertIsInstance(info.uuid, bytes)
+ self.assertEqual(len(info.uuid), 16)
+ self.assertEqual(info.parent_uuid, bytes(16))
+ self.assertEqual(info.received_uuid, bytes(16))
+ self.assertNotEqual(info.generation, 0)
+ self.assertNotEqual(info.ctransid, 0)
+ self.assertNotEqual(info.otransid, 0)
+ self.assertEqual(info.stransid, 0)
+ self.assertEqual(info.rtransid, 0)
+ self.assertNotEqual(info.ctime, 0)
+ self.assertNotEqual(info.otime, 0)
+ self.assertEqual(info.stime, 0)
+ self.assertEqual(info.rtime, 0)
+
+ subvol_uuid = info.uuid
+ snapshot = os.path.join(self.mountpoint, 'snapshot')
+ btrfsutil.create_snapshot(subvol, snapshot)
+
+ info = btrfsutil.subvolume_info(snapshot)
+ self.assertEqual(info.parent_uuid, subvol_uuid)
+
+ # TODO: test received_uuid, stransid, rtransid, stime, and rtime
+
+ for arg in self.path_or_fd(self.mountpoint):
+ with self.subTest(type=type(arg)):
+ with self.assertRaises(btrfsutil.BtrfsUtilError) as e:
+ # BTRFS_EXTENT_TREE_OBJECTID
+ btrfsutil.subvolume_info(arg, 2)
+
+ def test_read_only(self):
+ for arg in self.path_or_fd(self.mountpoint):
+ with self.subTest(type=type(arg)):
+ btrfsutil.set_subvolume_read_only(arg)
+ self.assertTrue(btrfsutil.get_subvolume_read_only(arg))
+ self.assertTrue(btrfsutil.subvolume_info(arg).flags & 1)
+
+ btrfsutil.set_subvolume_read_only(arg, False)
+ self.assertFalse(btrfsutil.get_subvolume_read_only(arg))
+ self.assertFalse(btrfsutil.subvolume_info(arg).flags & 1)
+
+ btrfsutil.set_subvolume_read_only(arg, True)
+ self.assertTrue(btrfsutil.get_subvolume_read_only(arg))
+ self.assertTrue(btrfsutil.subvolume_info(arg).flags & 1)
+
+ btrfsutil.set_subvolume_read_only(arg, False)
+
+ def test_default_subvolume(self):
+ for arg in self.path_or_fd(self.mountpoint):
+ with self.subTest(type=type(arg)):
+ self.assertEqual(btrfsutil.get_default_subvolume(arg), 5)
+
+ subvol = os.path.join(self.mountpoint, 'subvol')
+ btrfsutil.create_subvolume(subvol)
+ for arg in self.path_or_fd(subvol):
+ with self.subTest(type=type(arg)):
+ btrfsutil.set_default_subvolume(arg)
+ self.assertEqual(btrfsutil.get_default_subvolume(arg), 256)
+ btrfsutil.set_default_subvolume(arg, 5)
+ self.assertEqual(btrfsutil.get_default_subvolume(arg), 5)
+
+ def test_create_subvolume(self):
+ subvol = os.path.join(self.mountpoint, 'subvol')
+
+ btrfsutil.create_subvolume(subvol + '1')
+ self.assertTrue(btrfsutil.is_subvolume(subvol + '1'))
+ btrfsutil.create_subvolume((subvol + '2').encode())
+ self.assertTrue(btrfsutil.is_subvolume(subvol + '2'))
+ if HAVE_PATH_LIKE:
+ btrfsutil.create_subvolume(PurePath(subvol + '3'))
+ self.assertTrue(btrfsutil.is_subvolume(subvol + '3'))
+
+ pwd = os.getcwd()
+ try:
+ os.chdir(self.mountpoint)
+ btrfsutil.create_subvolume('subvol4')
+ self.assertTrue(btrfsutil.is_subvolume('subvol4'))
+ finally:
+ os.chdir(pwd)
+
+ btrfsutil.create_subvolume(subvol + '5/')
+ self.assertTrue(btrfsutil.is_subvolume(subvol + '5'))
+
+ btrfsutil.create_subvolume(subvol + '6//')
+ self.assertTrue(btrfsutil.is_subvolume(subvol + '6'))
+
+ transid = btrfsutil.create_subvolume(subvol + '7', async=True)
+ self.assertTrue(btrfsutil.is_subvolume(subvol + '7'))
+ self.assertGreater(transid, 0)
+
+ # Test creating subvolumes under '/' in a chroot.
+ pid = os.fork()
+ if pid == 0:
+ try:
+ os.chroot(self.mountpoint)
+ os.chdir('/')
+ btrfsutil.create_subvolume('/subvol8')
+ self.assertTrue(btrfsutil.is_subvolume('/subvol8'))
+ with self.assertRaises(btrfsutil.BtrfsUtilError):
+ btrfsutil.create_subvolume('/')
+ os._exit(0)
+ except Exception:
+ traceback.print_exc()
+ os._exit(1)
+ wstatus = os.waitpid(pid, 0)[1]
+ self.assertTrue(os.WIFEXITED(wstatus))
+ self.assertEqual(os.WEXITSTATUS(wstatus), 0)
+
+ def test_create_snapshot(self):
+ subvol = os.path.join(self.mountpoint, 'subvol')
+
+ btrfsutil.create_subvolume(subvol)
+ os.mkdir(os.path.join(subvol, 'dir'))
+
+ for i, arg in enumerate(self.path_or_fd(subvol)):
+ with self.subTest(type=type(arg)):
+ snapshots_dir = os.path.join(self.mountpoint, 'snapshots{}'.format(i))
+ os.mkdir(snapshots_dir)
+ snapshot = os.path.join(snapshots_dir, 'snapshot')
+
+ btrfsutil.create_snapshot(subvol, snapshot + '1')
+ self.assertTrue(btrfsutil.is_subvolume(snapshot + '1'))
+ self.assertTrue(os.path.exists(os.path.join(snapshot + '1', 'dir')))
+
+ btrfsutil.create_snapshot(subvol, (snapshot + '2').encode())
+ self.assertTrue(btrfsutil.is_subvolume(snapshot + '2'))
+ self.assertTrue(os.path.exists(os.path.join(snapshot + '2', 'dir')))
+
+ if HAVE_PATH_LIKE:
+ btrfsutil.create_snapshot(subvol, PurePath(snapshot + '3'))
+ self.assertTrue(btrfsutil.is_subvolume(snapshot + '3'))
+ self.assertTrue(os.path.exists(os.path.join(snapshot + '3', 'dir')))
+
+ nested_subvol = os.path.join(subvol, 'nested')
+ more_nested_subvol = os.path.join(nested_subvol, 'more_nested')
+ btrfsutil.create_subvolume(nested_subvol)
+ btrfsutil.create_subvolume(more_nested_subvol)
+ os.mkdir(os.path.join(more_nested_subvol, 'nested_dir'))
+
+ snapshot = os.path.join(self.mountpoint, 'snapshot')
+
+ btrfsutil.create_snapshot(subvol, snapshot + '1')
+ # Dummy subvolume.
+ self.assertEqual(os.stat(os.path.join(snapshot + '1', 'nested')).st_ino, 2)
+ self.assertFalse(os.path.exists(os.path.join(snapshot + '1', 'nested', 'more_nested')))
+
+ btrfsutil.create_snapshot(subvol, snapshot + '2', recursive=True)
+ self.assertTrue(os.path.exists(os.path.join(snapshot + '2', 'nested/more_nested/nested_dir')))
+
+ transid = btrfsutil.create_snapshot(subvol, snapshot + '3', recursive=True, async=True)
+ self.assertTrue(os.path.exists(os.path.join(snapshot + '3', 'nested/more_nested/nested_dir')))
+ self.assertGreater(transid, 0)
+
+ btrfsutil.create_snapshot(subvol, snapshot + '4', read_only=True)
+ self.assertTrue(btrfsutil.get_subvolume_read_only(snapshot + '4'))
+
+ def test_delete_subvolume(self):
+ subvol = os.path.join(self.mountpoint, 'subvol')
+ btrfsutil.create_subvolume(subvol + '1')
+ self.assertTrue(os.path.exists(subvol + '1'))
+ btrfsutil.create_subvolume(subvol + '2')
+ self.assertTrue(os.path.exists(subvol + '2'))
+ btrfsutil.create_subvolume(subvol + '3')
+ self.assertTrue(os.path.exists(subvol + '3'))
+
+ btrfsutil.delete_subvolume(subvol + '1')
+ self.assertFalse(os.path.exists(subvol + '1'))
+ btrfsutil.delete_subvolume((subvol + '2').encode())
+ self.assertFalse(os.path.exists(subvol + '2'))
+ if HAVE_PATH_LIKE:
+ btrfsutil.delete_subvolume(PurePath(subvol + '3'))
+ self.assertFalse(os.path.exists(subvol + '3'))
+
+ # Test deleting subvolumes under '/' in a chroot.
+ pid = os.fork()
+ if pid == 0:
+ try:
+ os.chroot(self.mountpoint)
+ os.chdir('/')
+ btrfsutil.create_subvolume('/subvol4')
+ self.assertTrue(os.path.exists('/subvol4'))
+ btrfsutil.delete_subvolume('/subvol4')
+ self.assertFalse(os.path.exists('/subvol4'))
+ with self.assertRaises(btrfsutil.BtrfsUtilError):
+ btrfsutil.delete_subvolume('/')
+ os._exit(0)
+ except Exception:
+ traceback.print_exc()
+ os._exit(1)
+ wstatus = os.waitpid(pid, 0)[1]
+ self.assertTrue(os.WIFEXITED(wstatus))
+ self.assertEqual(os.WEXITSTATUS(wstatus), 0)
+
+ btrfsutil.create_subvolume(subvol + '5')
+ btrfsutil.create_subvolume(subvol + '5/foo')
+ btrfsutil.create_subvolume(subvol + '5/bar')
+ btrfsutil.create_subvolume(subvol + '5/bar/baz')
+ btrfsutil.create_subvolume(subvol + '5/bar/qux')
+ btrfsutil.create_subvolume(subvol + '5/quux')
+ with self.assertRaises(btrfsutil.BtrfsUtilError):
+ btrfsutil.delete_subvolume(subvol + '5')
+ btrfsutil.delete_subvolume(subvol + '5', recursive=True)
+ self.assertFalse(os.path.exists(subvol + '5'))
+
+ def test_deleted_subvolumes(self):
+ subvol = os.path.join(self.mountpoint, 'subvol')
+ btrfsutil.create_subvolume(subvol + '1')
+ btrfsutil.delete_subvolume(subvol + '1')
+ for arg in self.path_or_fd(self.mountpoint):
+ with self.subTest(type=type(arg)):
+ self.assertEqual(btrfsutil.deleted_subvolumes(arg), [256])
+
+ def test_subvolume_iterator(self):
+ pwd = os.getcwd()
+ try:
+ os.chdir(self.mountpoint)
+ btrfsutil.create_subvolume('foo')
+
+ path, subvol = next(btrfsutil.SubvolumeIterator('.', info=True))
+ self.assertEqual(path, 'foo')
+ self.assertIsInstance(subvol, btrfsutil.SubvolumeInfo)
+ self.assertEqual(subvol.id, 256)
+ self.assertEqual(subvol.parent_id, 5)
+
+ btrfsutil.create_subvolume('foo/bar')
+ btrfsutil.create_subvolume('foo/bar/baz')
+
+ subvols = [
+ ('foo', 256),
+ ('foo/bar', 257),
+ ('foo/bar/baz', 258),
+ ]
+
+ for arg in self.path_or_fd('.'):
+ with self.subTest(type=type(arg)):
+ self.assertEqual(list(btrfsutil.SubvolumeIterator(arg)), subvols)
+ self.assertEqual(list(btrfsutil.SubvolumeIterator('.', top=0)), subvols)
+
+ self.assertEqual(list(btrfsutil.SubvolumeIterator('.', post_order=True)),
+ [('foo/bar/baz', 258),
+ ('foo/bar', 257),
+ ('foo', 256)])
+
+ subvols = [
+ ('bar', 257),
+ ('bar/baz', 258),
+ ]
+
+ self.assertEqual(list(btrfsutil.SubvolumeIterator('.', top=256)), subvols)
+ self.assertEqual(list(btrfsutil.SubvolumeIterator('foo', top=0)), subvols)
+
+ os.rename('foo/bar/baz', 'baz')
+ self.assertEqual(sorted(btrfsutil.SubvolumeIterator('.')),
+ [('baz', 258),
+ ('foo', 256),
+ ('foo/bar', 257)])
+
+ with btrfsutil.SubvolumeIterator('.') as it:
+ self.assertGreaterEqual(it.fileno(), 0)
+ it.close()
+ with self.assertRaises(ValueError):
+ next(iter(it))
+ with self.assertRaises(ValueError):
+ it.fileno()
+ it.close()
+ finally:
+ os.chdir(pwd)
diff --git a/libbtrfsutil/qgroup.c b/libbtrfsutil/qgroup.c
new file mode 100644
index 00000000..46a9043b
--- /dev/null
+++ b/libbtrfsutil/qgroup.c
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2018 Facebook
+ *
+ * This file is part of libbtrfsutil.
+ *
+ * libbtrfsutil is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libbtrfsutil 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libbtrfsutil. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "btrfsutil_internal.h"
+
+PUBLIC enum btrfs_util_error btrfs_util_create_qgroup_inherit(int flags,
+ struct btrfs_util_qgroup_inherit **ret)
+{
+ struct btrfs_qgroup_inherit *inherit;
+
+ if (flags) {
+ errno = EINVAL;
+ return BTRFS_UTIL_ERROR_INVALID_ARGUMENT;
+ }
+
+ inherit = calloc(1, sizeof(*inherit));
+ if (!inherit)
+ return BTRFS_UTIL_ERROR_NO_MEMORY;
+
+ /*
+ * struct btrfs_util_qgroup_inherit is a lie; it's actually struct
+ * btrfs_qgroup_inherit, but we abstract it away so that users don't
+ * need to depend on the Btrfs UAPI headers.
+ */
+ *ret = (struct btrfs_util_qgroup_inherit *)inherit;
+
+ return BTRFS_UTIL_OK;
+}
+
+PUBLIC void btrfs_util_destroy_qgroup_inherit(struct btrfs_util_qgroup_inherit *inherit)
+{
+ free(inherit);
+}
+
+PUBLIC enum btrfs_util_error btrfs_util_qgroup_inherit_add_group(struct btrfs_util_qgroup_inherit **inherit,
+ uint64_t qgroupid)
+{
+ struct btrfs_qgroup_inherit *tmp = (struct btrfs_qgroup_inherit *)*inherit;
+
+ tmp = realloc(tmp, sizeof(*tmp) +
+ (tmp->num_qgroups + 1) * sizeof(tmp->qgroups[0]));
+ if (!tmp)
+ return BTRFS_UTIL_ERROR_NO_MEMORY;
+
+ tmp->qgroups[tmp->num_qgroups++] = qgroupid;
+
+ *inherit = (struct btrfs_util_qgroup_inherit *)tmp;
+
+ return BTRFS_UTIL_OK;
+}
+
+PUBLIC void btrfs_util_qgroup_inherit_get_groups(const struct btrfs_util_qgroup_inherit *inherit,
+ const uint64_t **groups,
+ size_t *n)
+{
+ struct btrfs_qgroup_inherit *tmp = (struct btrfs_qgroup_inherit *)inherit;
+
+ /* Need to cast because __u64 != uint64_t. */
+ *groups = (const uint64_t *)&tmp->qgroups[0];
+ *n = tmp->num_qgroups;
+}
diff --git a/libbtrfsutil/stubs.c b/libbtrfsutil/stubs.c
new file mode 100644
index 00000000..9b9e037f
--- /dev/null
+++ b/libbtrfsutil/stubs.c
@@ -0,0 +1,35 @@
+/*
+ * This file is part of libbtrfsutil.
+ *
+ * libbtrfsutil is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libbtrfsutil 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libbtrfsutil. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if HAVE_REALLOCARRAY != 1
+
+#include <stdlib.h>
+#include <errno.h>
+
+void *reallocarray(void *ptr, size_t nmemb, size_t size)
+{
+ size_t res;
+
+ res = nmemb * size;
+ if (res < nmemb || res < size) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ return realloc(ptr, res);
+}
+
+#endif
diff --git a/libbtrfsutil/stubs.h b/libbtrfsutil/stubs.h
new file mode 100644
index 00000000..cb6d43ca
--- /dev/null
+++ b/libbtrfsutil/stubs.h
@@ -0,0 +1,22 @@
+/*
+ * This file is part of libbtrfsutil.
+ *
+ * libbtrfsutil is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libbtrfsutil 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libbtrfsutil. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _LIBBTRFSUTIL_STUBS_H_
+
+void *reallocarray(void *ptr, size_t nmemb, size_t size);
+
+#endif
diff --git a/libbtrfsutil/subvolume.c b/libbtrfsutil/subvolume.c
new file mode 100644
index 00000000..867b3e10
--- /dev/null
+++ b/libbtrfsutil/subvolume.c
@@ -0,0 +1,1382 @@
+/*
+ * Copyright (C) 2018 Facebook
+ *
+ * This file is part of libbtrfsutil.
+ *
+ * libbtrfsutil is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libbtrfsutil 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libbtrfsutil. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/vfs.h>
+#include <linux/magic.h>
+#include "stubs.h"
+
+#include "btrfsutil_internal.h"
+
+/*
+ * This intentionally duplicates btrfs_util_is_subvolume_fd() instead of opening
+ * a file descriptor and calling it, because fstat() and fstatfs() don't accept
+ * file descriptors opened with O_PATH on old kernels (before v3.6 and before
+ * v3.12, respectively), but stat() and statfs() can be called on a path that
+ * the user doesn't have read or write permissions to.
+ */
+PUBLIC enum btrfs_util_error btrfs_util_is_subvolume(const char *path)
+{
+ struct statfs sfs;
+ struct stat st;
+ int ret;
+
+ ret = statfs(path, &sfs);
+ if (ret == -1)
+ return BTRFS_UTIL_ERROR_STATFS_FAILED;
+
+ if (sfs.f_type != BTRFS_SUPER_MAGIC) {
+ errno = EINVAL;
+ return BTRFS_UTIL_ERROR_NOT_BTRFS;
+ }
+
+ ret = stat(path, &st);
+ if (ret == -1)
+ return BTRFS_UTIL_ERROR_STAT_FAILED;
+
+ if (st.st_ino != BTRFS_FIRST_FREE_OBJECTID || !S_ISDIR(st.st_mode)) {
+ errno = EINVAL;
+ return BTRFS_UTIL_ERROR_NOT_SUBVOLUME;
+ }
+
+ return BTRFS_UTIL_OK;
+}
+
+PUBLIC enum btrfs_util_error btrfs_util_is_subvolume_fd(int fd)
+{
+ struct statfs sfs;
+ struct stat st;
+ int ret;
+
+ ret = fstatfs(fd, &sfs);
+ if (ret == -1)
+ return BTRFS_UTIL_ERROR_STATFS_FAILED;
+
+ if (sfs.f_type != BTRFS_SUPER_MAGIC) {
+ errno = EINVAL;
+ return BTRFS_UTIL_ERROR_NOT_BTRFS;
+ }
+
+ ret = fstat(fd, &st);
+ if (ret == -1)
+ return BTRFS_UTIL_ERROR_STAT_FAILED;
+
+ if (st.st_ino != BTRFS_FIRST_FREE_OBJECTID || !S_ISDIR(st.st_mode)) {
+ errno = EINVAL;
+ return BTRFS_UTIL_ERROR_NOT_SUBVOLUME;
+ }
+
+ return BTRFS_UTIL_OK;
+}
+
+PUBLIC enum btrfs_util_error btrfs_util_subvolume_id(const char *path,
+ uint64_t *id_ret)
+{
+ enum btrfs_util_error err;
+ int fd;
+
+ fd = open(path, O_RDONLY);
+ if (fd == -1)
+ return BTRFS_UTIL_ERROR_OPEN_FAILED;
+
+ err = btrfs_util_subvolume_id_fd(fd, id_ret);
+ SAVE_ERRNO_AND_CLOSE(fd);
+ return err;
+}
+
+PUBLIC enum btrfs_util_error btrfs_util_subvolume_id_fd(int fd,
+ uint64_t *id_ret)
+{
+ struct btrfs_ioctl_ino_lookup_args args = {
+ .treeid = 0,
+ .objectid = BTRFS_FIRST_FREE_OBJECTID,
+ };
+ int ret;
+
+ ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args);
+ if (ret == -1) {
+ close(fd);
+ return BTRFS_UTIL_ERROR_INO_LOOKUP_FAILED;
+ }
+
+ *id_ret = args.treeid;
+
+ return BTRFS_UTIL_OK;
+}
+
+PUBLIC enum btrfs_util_error btrfs_util_subvolume_path(const char *path,
+ uint64_t id,
+ char **path_ret)
+{
+ enum btrfs_util_error err;
+ int fd;
+
+ fd = open(path, O_RDONLY);
+ if (fd == -1)
+ return BTRFS_UTIL_ERROR_OPEN_FAILED;
+
+ err = btrfs_util_subvolume_path_fd(fd, id, path_ret);
+ SAVE_ERRNO_AND_CLOSE(fd);
+ return err;
+}
+
+PUBLIC enum btrfs_util_error btrfs_util_subvolume_path_fd(int fd, uint64_t id,
+ char **path_ret)
+{
+ char *path, *p;
+ size_t capacity = 4096;
+
+ if (id == 0) {
+ enum btrfs_util_error err;
+
+ err = btrfs_util_is_subvolume_fd(fd);
+ if (err)
+ return err;
+
+ err = btrfs_util_subvolume_id_fd(fd, &id);
+ if (err)
+ return err;
+ }
+
+ path = malloc(capacity);
+ if (!path)
+ return BTRFS_UTIL_ERROR_NO_MEMORY;
+ p = path + capacity - 1;
+ p[0] = '\0';
+
+ while (id != BTRFS_FS_TREE_OBJECTID) {
+ struct btrfs_ioctl_search_args search = {
+ .key = {
+ .tree_id = BTRFS_ROOT_TREE_OBJECTID,
+ .min_objectid = id,
+ .max_objectid = id,
+ .min_type = BTRFS_ROOT_BACKREF_KEY,
+ .max_type = BTRFS_ROOT_BACKREF_KEY,
+ .min_offset = 0,
+ .max_offset = UINT64_MAX,
+ .min_transid = 0,
+ .max_transid = UINT64_MAX,
+ .nr_items = 1,
+ },
+ };
+ struct btrfs_ioctl_ino_lookup_args lookup;
+ const struct btrfs_ioctl_search_header *header;
+ const struct btrfs_root_ref *ref;
+ const char *name;
+ uint16_t name_len;
+ size_t lookup_len;
+ size_t total_len;
+ int ret;
+
+ ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &search);
+ if (ret == -1) {
+ free(path);
+ return BTRFS_UTIL_ERROR_SEARCH_FAILED;
+ }
+
+ if (search.key.nr_items == 0) {
+ free(path);
+ errno = ENOENT;
+ return BTRFS_UTIL_ERROR_SUBVOLUME_NOT_FOUND;
+ }
+
+ header = (struct btrfs_ioctl_search_header *)search.buf;
+ ref = (struct btrfs_root_ref *)(header + 1);
+ name = (char *)(ref + 1);
+ name_len = le16_to_cpu(ref->name_len);
+
+ id = header->offset;
+
+ lookup.treeid = id;
+ lookup.objectid = le64_to_cpu(ref->dirid);
+ ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &lookup);
+ if (ret == -1) {
+ free(path);
+ return BTRFS_UTIL_ERROR_SEARCH_FAILED;
+ }
+ lookup_len = strlen(lookup.name);
+
+ total_len = name_len + lookup_len + (id != BTRFS_FS_TREE_OBJECTID);
+ if (p - total_len < path) {
+ char *new_path, *new_p;
+ size_t new_capacity = capacity * 2;
+
+ new_path = malloc(new_capacity);
+ if (!new_path) {
+ free(path);
+ return BTRFS_UTIL_ERROR_NO_MEMORY;
+ }
+ new_p = new_path + new_capacity - (path + capacity - p);
+ memcpy(new_p, p, path + capacity - p);
+ free(path);
+ path = new_path;
+ p = new_p;
+ capacity = new_capacity;
+ }
+ p -= name_len;
+ memcpy(p, name, name_len);
+ p -= lookup_len;
+ memcpy(p, lookup.name, lookup_len);
+ if (id != BTRFS_FS_TREE_OBJECTID)
+ *--p = '/';
+ }
+
+ if (p != path)
+ memmove(path, p, path + capacity - p);
+
+ *path_ret = path;
+
+ return BTRFS_UTIL_OK;
+}
+
+static void copy_timespec(struct timespec *timespec,
+ const struct btrfs_timespec *btrfs_timespec)
+{
+ timespec->tv_sec = le64_to_cpu(btrfs_timespec->sec);
+ timespec->tv_nsec = le32_to_cpu(btrfs_timespec->nsec);
+}
+
+static void copy_root_item(struct btrfs_util_subvolume_info *subvol,
+ const struct btrfs_root_item *root)
+{
+ subvol->flags = le64_to_cpu(root->flags);
+ memcpy(subvol->uuid, root->uuid, sizeof(subvol->uuid));
+ memcpy(subvol->parent_uuid, root->parent_uuid,
+ sizeof(subvol->parent_uuid));
+ memcpy(subvol->received_uuid, root->received_uuid,
+ sizeof(subvol->received_uuid));
+ subvol->generation = le64_to_cpu(root->generation);
+ subvol->ctransid = le64_to_cpu(root->ctransid);
+ subvol->otransid = le64_to_cpu(root->otransid);
+ subvol->stransid = le64_to_cpu(root->stransid);
+ subvol->rtransid = le64_to_cpu(root->rtransid);
+ copy_timespec(&subvol->ctime, &root->ctime);
+ copy_timespec(&subvol->otime, &root->otime);
+ copy_timespec(&subvol->stime, &root->stime);
+ copy_timespec(&subvol->rtime, &root->rtime);
+}
+
+PUBLIC enum btrfs_util_error btrfs_util_subvolume_info(const char *path,
+ uint64_t id,
+ struct btrfs_util_subvolume_info *subvol)
+{
+ enum btrfs_util_error err;
+ int fd;
+
+ fd = open(path, O_RDONLY);
+ if (fd == -1)
+ return BTRFS_UTIL_ERROR_OPEN_FAILED;
+
+ err = btrfs_util_subvolume_info_fd(fd, id, subvol);
+ SAVE_ERRNO_AND_CLOSE(fd);
+ return err;
+}
+
+PUBLIC enum btrfs_util_error btrfs_util_subvolume_info_fd(int fd, uint64_t id,
+ struct btrfs_util_subvolume_info *subvol)
+{
+ struct btrfs_ioctl_search_args search = {
+ .key = {
+ .tree_id = BTRFS_ROOT_TREE_OBJECTID,
+ .min_type = BTRFS_ROOT_ITEM_KEY,
+ .max_type = BTRFS_ROOT_BACKREF_KEY,
+ .min_offset = 0,
+ .max_offset = UINT64_MAX,
+ .min_transid = 0,
+ .max_transid = UINT64_MAX,
+ .nr_items = 0,
+ },
+ };
+ enum btrfs_util_error err;
+ size_t items_pos = 0, buf_off = 0;
+ bool need_root_item = true, need_root_backref = true;
+ int ret;
+
+ if (id == 0) {
+ err = btrfs_util_is_subvolume_fd(fd);
+ if (err)
+ return err;
+
+ err = btrfs_util_subvolume_id_fd(fd, &id);
+ if (err)
+ return err;
+ }
+
+ if ((id < BTRFS_FIRST_FREE_OBJECTID && id != BTRFS_FS_TREE_OBJECTID) ||
+ id > BTRFS_LAST_FREE_OBJECTID) {
+ errno = ENOENT;
+ return BTRFS_UTIL_ERROR_SUBVOLUME_NOT_FOUND;
+ }
+
+ search.key.min_objectid = search.key.max_objectid = id;
+
+ if (subvol) {
+ subvol->id = id;
+ subvol->parent_id = 0;
+ subvol->dir_id = 0;
+ if (id == BTRFS_FS_TREE_OBJECTID)
+ need_root_backref = false;
+ } else {
+ /*
+ * We only need the backref for filling in the subvolume info.
+ */
+ need_root_backref = false;
+ }
+
+ /* Don't bother searching for the backref if we don't need it. */
+ if (!need_root_backref)
+ search.key.max_type = BTRFS_ROOT_ITEM_KEY;
+
+ while (need_root_item || need_root_backref) {
+ const struct btrfs_ioctl_search_header *header;
+
+ if (items_pos >= search.key.nr_items) {
+ search.key.nr_items = 4096;
+ ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &search);
+ if (ret == -1)
+ return BTRFS_UTIL_ERROR_SEARCH_FAILED;
+ items_pos = 0;
+ buf_off = 0;
+
+ if (search.key.nr_items == 0) {
+ if (need_root_item) {
+ errno = ENOENT;
+ return BTRFS_UTIL_ERROR_SUBVOLUME_NOT_FOUND;
+ } else {
+ break;
+ }
+ }
+ }
+
+ header = (struct btrfs_ioctl_search_header *)(search.buf + buf_off);
+ if (header->type == BTRFS_ROOT_ITEM_KEY) {
+ if (subvol) {
+ const struct btrfs_root_item *root;
+
+ root = (const struct btrfs_root_item *)(header + 1);
+ copy_root_item(subvol, root);
+ }
+ need_root_item = false;
+ search.key.min_type = BTRFS_ROOT_BACKREF_KEY;
+ } else if (header->type == BTRFS_ROOT_BACKREF_KEY) {
+ if (subvol) {
+ const struct btrfs_root_ref *ref;
+
+ ref = (const struct btrfs_root_ref *)(header + 1);
+ subvol->parent_id = header->offset;
+ subvol->dir_id = le64_to_cpu(ref->dirid);
+ }
+ need_root_backref = false;
+ search.key.min_type = UINT32_MAX;
+ }
+
+ items_pos++;
+ buf_off += sizeof(*header) + header->len;
+ }
+
+ return BTRFS_UTIL_OK;
+}
+
+PUBLIC enum btrfs_util_error btrfs_util_get_subvolume_read_only_fd(int fd,
+ bool *read_only_ret)
+{
+ uint64_t flags;
+ int ret;
+
+ ret = ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags);
+ if (ret == -1)
+ return BTRFS_UTIL_ERROR_SUBVOL_GETFLAGS_FAILED;
+
+ *read_only_ret = flags & BTRFS_SUBVOL_RDONLY;
+ return BTRFS_UTIL_OK;
+}
+
+PUBLIC enum btrfs_util_error btrfs_util_get_subvolume_read_only(const char *path,
+ bool *ret)
+{
+ enum btrfs_util_error err;
+ int fd;
+
+ fd = open(path, O_RDONLY);
+ if (fd == -1)
+ return BTRFS_UTIL_ERROR_OPEN_FAILED;
+
+ err = btrfs_util_get_subvolume_read_only_fd(fd, ret);
+ SAVE_ERRNO_AND_CLOSE(fd);
+ return err;
+}
+
+PUBLIC enum btrfs_util_error btrfs_util_set_subvolume_read_only(const char *path,
+ bool read_only)
+{
+ enum btrfs_util_error err;
+ int fd;
+
+ fd = open(path, O_RDONLY);
+ if (fd == -1)
+ return BTRFS_UTIL_ERROR_OPEN_FAILED;
+
+ err = btrfs_util_set_subvolume_read_only_fd(fd, read_only);
+ SAVE_ERRNO_AND_CLOSE(fd);
+ return err;
+}
+
+PUBLIC enum btrfs_util_error btrfs_util_set_subvolume_read_only_fd(int fd,
+ bool read_only)
+{
+ uint64_t flags;
+ int ret;
+
+ ret = ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags);
+ if (ret == -1)
+ return BTRFS_UTIL_ERROR_SUBVOL_GETFLAGS_FAILED;
+
+ if (read_only)
+ flags |= BTRFS_SUBVOL_RDONLY;
+ else
+ flags &= ~BTRFS_SUBVOL_RDONLY;
+
+ ret = ioctl(fd, BTRFS_IOC_SUBVOL_SETFLAGS, &flags);
+ if (ret == -1)
+ return BTRFS_UTIL_ERROR_SUBVOL_SETFLAGS_FAILED;
+
+ return BTRFS_UTIL_OK;
+}
+
+PUBLIC enum btrfs_util_error btrfs_util_get_default_subvolume(const char *path,
+ uint64_t *id_ret)
+{
+ enum btrfs_util_error err;
+ int fd;
+
+ fd = open(path, O_RDONLY);
+ if (fd == -1)
+ return BTRFS_UTIL_ERROR_OPEN_FAILED;
+
+ err = btrfs_util_get_default_subvolume_fd(fd, id_ret);
+ SAVE_ERRNO_AND_CLOSE(fd);
+ return err;
+}
+
+PUBLIC enum btrfs_util_error btrfs_util_get_default_subvolume_fd(int fd,
+ uint64_t *id_ret)
+{
+ struct btrfs_ioctl_search_args search = {
+ .key = {
+ .tree_id = BTRFS_ROOT_TREE_OBJECTID,
+ .min_objectid = BTRFS_ROOT_TREE_DIR_OBJECTID,
+ .max_objectid = BTRFS_ROOT_TREE_DIR_OBJECTID,
+ .min_type = BTRFS_DIR_ITEM_KEY,
+ .max_type = BTRFS_DIR_ITEM_KEY,
+ .min_offset = 0,
+ .max_offset = UINT64_MAX,
+ .min_transid = 0,
+ .max_transid = UINT64_MAX,
+ .nr_items = 0,
+ },
+ };
+ size_t items_pos = 0, buf_off = 0;
+ int ret;
+
+ for (;;) {
+ const struct btrfs_ioctl_search_header *header;
+
+ if (items_pos >= search.key.nr_items) {
+ search.key.nr_items = 4096;
+ ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &search);
+ if (ret == -1)
+ return BTRFS_UTIL_ERROR_SEARCH_FAILED;
+ items_pos = 0;
+ buf_off = 0;
+
+ if (search.key.nr_items == 0) {
+ errno = ENOENT;
+ return BTRFS_UTIL_ERROR_SUBVOLUME_NOT_FOUND;
+ }
+ }
+
+ header = (struct btrfs_ioctl_search_header *)(search.buf + buf_off);
+ if (header->type == BTRFS_DIR_ITEM_KEY) {
+ const struct btrfs_dir_item *dir;
+ const char *name;
+ uint16_t name_len;
+
+ dir = (struct btrfs_dir_item *)(header + 1);
+ name = (const char *)(dir + 1);
+ name_len = le16_to_cpu(dir->name_len);
+ if (strncmp(name, "default", name_len) == 0) {
+ *id_ret = le64_to_cpu(dir->location.objectid);
+ break;
+ }
+ }
+
+ items_pos++;
+ buf_off += sizeof(*header) + header->len;
+ search.key.min_offset = header->offset + 1;
+ }
+
+ return BTRFS_UTIL_OK;
+}
+
+PUBLIC enum btrfs_util_error btrfs_util_set_default_subvolume(const char *path,
+ uint64_t id)
+{
+ enum btrfs_util_error err;
+ int fd;
+
+ fd = open(path, O_RDONLY);
+ if (fd == -1)
+ return BTRFS_UTIL_ERROR_OPEN_FAILED;
+
+ err = btrfs_util_set_default_subvolume_fd(fd, id);
+ SAVE_ERRNO_AND_CLOSE(fd);
+ return err;
+}
+
+PUBLIC enum btrfs_util_error btrfs_util_set_default_subvolume_fd(int fd,
+ uint64_t id)
+{
+ enum btrfs_util_error err;
+ int ret;
+
+ if (id == 0) {
+ err = btrfs_util_is_subvolume_fd(fd);
+ if (err)
+ return err;
+
+ err = btrfs_util_subvolume_id_fd(fd, &id);
+ if (err)
+ return err;
+ }
+
+ ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &id);
+ if (ret == -1)
+ return BTRFS_UTIL_ERROR_DEFAULT_SUBVOL_FAILED;
+
+ return BTRFS_UTIL_OK;
+}
+
+static enum btrfs_util_error openat_parent_and_name(int dirfd, const char *path,
+ char *name, size_t name_len,
+ int *fd)
+{
+ char *tmp_path, *slash, *dirname, *basename;
+ size_t len;
+
+ /* Ignore trailing slashes. */
+ len = strlen(path);
+ while (len > 1 && path[len - 1] == '/')
+ len--;
+
+ tmp_path = malloc(len + 1);
+ if (!tmp_path)
+ return BTRFS_UTIL_ERROR_NO_MEMORY;
+ memcpy(tmp_path, path, len);
+ tmp_path[len] = '\0';
+
+ slash = memrchr(tmp_path, '/', len);
+ if (slash == tmp_path) {
+ dirname = "/";
+ basename = tmp_path + 1;
+ } else if (slash) {
+ *slash = '\0';
+ dirname = tmp_path;
+ basename = slash + 1;
+ } else {
+ dirname = ".";
+ basename = tmp_path;
+ }
+
+ len = strlen(basename);
+ if (len >= name_len) {
+ free(tmp_path);
+ errno = ENAMETOOLONG;
+ return BTRFS_UTIL_ERROR_INVALID_ARGUMENT;
+ }
+ memcpy(name, basename, len);
+ name[len] = '\0';
+
+ *fd = openat(dirfd, dirname, O_RDONLY | O_DIRECTORY);
+ if (*fd == -1) {
+ free(tmp_path);
+ return BTRFS_UTIL_ERROR_OPEN_FAILED;
+ }
+
+ free(tmp_path);
+ return BTRFS_UTIL_OK;
+}
+
+PUBLIC enum btrfs_util_error btrfs_util_create_subvolume(const char *path,
+ int flags,
+ uint64_t *async_transid,
+ struct btrfs_util_qgroup_inherit *qgroup_inherit)
+{
+ char name[BTRFS_SUBVOL_NAME_MAX + 1];
+ enum btrfs_util_error err;
+ int parent_fd;
+
+ err = openat_parent_and_name(AT_FDCWD, path, name, sizeof(name),
+ &parent_fd);
+ if (err)
+ return err;
+
+ err = btrfs_util_create_subvolume_fd(parent_fd, name, flags,
+ async_transid, qgroup_inherit);
+ SAVE_ERRNO_AND_CLOSE(parent_fd);
+ return err;
+}
+
+PUBLIC enum btrfs_util_error btrfs_util_create_subvolume_fd(int parent_fd,
+ const char *name,
+ int flags,
+ uint64_t *async_transid,
+ struct btrfs_util_qgroup_inherit *qgroup_inherit)
+{
+ struct btrfs_ioctl_vol_args_v2 args = {};
+ size_t len;
+ int ret;
+
+ if (flags) {
+ errno = EINVAL;
+ return BTRFS_UTIL_ERROR_INVALID_ARGUMENT;
+ }
+
+ if (async_transid)
+ args.flags |= BTRFS_SUBVOL_CREATE_ASYNC;
+ if (qgroup_inherit) {
+ args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
+ args.qgroup_inherit = (struct btrfs_qgroup_inherit *)qgroup_inherit;
+ args.size = (sizeof(*args.qgroup_inherit) +
+ args.qgroup_inherit->num_qgroups *
+ sizeof(args.qgroup_inherit->qgroups[0]));
+ }
+
+ len = strlen(name);
+ if (len >= sizeof(args.name)) {
+ errno = ENAMETOOLONG;
+ return BTRFS_UTIL_ERROR_INVALID_ARGUMENT;
+ }
+ memcpy(args.name, name, len);
+ args.name[len] = '\0';
+
+ ret = ioctl(parent_fd, BTRFS_IOC_SUBVOL_CREATE_V2, &args);
+ if (ret == -1)
+ return BTRFS_UTIL_ERROR_SUBVOL_CREATE_FAILED;
+
+ if (async_transid)
+ *async_transid = args.transid;
+
+ return BTRFS_UTIL_OK;
+}
+
+#define BTRFS_UTIL_SUBVOLUME_ITERATOR_CLOSE_FD (1 << 30)
+
+struct search_stack_entry {
+ struct btrfs_ioctl_search_args search;
+ size_t items_pos, buf_off;
+ size_t path_len;
+};
+
+struct btrfs_util_subvolume_iterator {
+ int fd;
+ int flags;
+
+ struct search_stack_entry *search_stack;
+ size_t search_stack_len;
+ size_t search_stack_capacity;
+
+ char *cur_path;
+ size_t cur_path_capacity;
+};
+
+static enum btrfs_util_error append_to_search_stack(struct btrfs_util_subvolume_iterator *iter,
+ uint64_t tree_id,
+ size_t path_len)
+{
+ struct search_stack_entry *entry;
+
+ if (iter->search_stack_len >= iter->search_stack_capacity) {
+ size_t new_capacity = iter->search_stack_capacity * 2;
+ struct search_stack_entry *new_search_stack;
+
+ new_search_stack = reallocarray(iter->search_stack,
+ new_capacity,
+ sizeof(*iter->search_stack));
+ if (!new_search_stack)
+ return BTRFS_UTIL_ERROR_NO_MEMORY;
+
+ iter->search_stack_capacity = new_capacity;
+ iter->search_stack = new_search_stack;
+ }
+
+ entry = &iter->search_stack[iter->search_stack_len++];
+
+ memset(&entry->search, 0, sizeof(entry->search));
+ entry->search.key.tree_id = BTRFS_ROOT_TREE_OBJECTID;
+ entry->search.key.min_objectid = tree_id;
+ entry->search.key.max_objectid = tree_id;
+ entry->search.key.min_type = BTRFS_ROOT_REF_KEY;
+ entry->search.key.max_type = BTRFS_ROOT_REF_KEY;
+ entry->search.key.min_offset = 0;
+ entry->search.key.max_offset = UINT64_MAX;
+ entry->search.key.min_transid = 0;
+ entry->search.key.max_transid = UINT64_MAX;
+ entry->search.key.nr_items = 0;
+
+ entry->items_pos = 0;
+ entry->buf_off = 0;
+
+ entry->path_len = path_len;
+
+ return BTRFS_UTIL_OK;
+}
+
+PUBLIC enum btrfs_util_error btrfs_util_create_subvolume_iterator(const char *path,
+ uint64_t top,
+ int flags,
+ struct btrfs_util_subvolume_iterator **ret)
+{
+ enum btrfs_util_error err;
+ int fd;
+
+ fd = open(path, O_RDONLY);
+ if (fd == -1)
+ return BTRFS_UTIL_ERROR_OPEN_FAILED;
+
+ err = btrfs_util_create_subvolume_iterator_fd(fd, top, flags, ret);
+ if (err)
+ SAVE_ERRNO_AND_CLOSE(fd);
+ else
+ (*ret)->flags |= BTRFS_UTIL_SUBVOLUME_ITERATOR_CLOSE_FD;
+
+ return err;
+}
+
+PUBLIC enum btrfs_util_error btrfs_util_create_subvolume_iterator_fd(int fd,
+ uint64_t top,
+ int flags,
+ struct btrfs_util_subvolume_iterator **ret)
+{
+ struct btrfs_util_subvolume_iterator *iter;
+ enum btrfs_util_error err;
+
+ if (flags & ~BTRFS_UTIL_SUBVOLUME_ITERATOR_MASK) {
+ errno = EINVAL;
+ return BTRFS_UTIL_ERROR_INVALID_ARGUMENT;
+ }
+
+ if (top == 0) {
+ err = btrfs_util_is_subvolume_fd(fd);
+ if (err)
+ return err;
+
+ err = btrfs_util_subvolume_id_fd(fd, &top);
+ if (err)
+ return err;
+ }
+
+ iter = malloc(sizeof(*iter));
+ if (!iter)
+ return BTRFS_UTIL_ERROR_NO_MEMORY;
+
+ iter->fd = fd;
+ iter->flags = flags;
+
+ iter->search_stack_len = 0;
+ iter->search_stack_capacity = 4;
+ iter->search_stack = malloc(sizeof(*iter->search_stack) *
+ iter->search_stack_capacity);
+ if (!iter->search_stack) {
+ err = BTRFS_UTIL_ERROR_NO_MEMORY;
+ goto out_iter;
+ }
+
+ iter->cur_path_capacity = 256;
+ iter->cur_path = malloc(iter->cur_path_capacity);
+ if (!iter->cur_path) {
+ err = BTRFS_UTIL_ERROR_NO_MEMORY;
+ goto out_search_stack;
+ }
+
+ err = append_to_search_stack(iter, top, 0);
+ if (err)
+ goto out_cur_path;
+
+ *ret = iter;
+
+ return BTRFS_UTIL_OK;
+
+out_cur_path:
+ free(iter->cur_path);
+out_search_stack:
+ free(iter->search_stack);
+out_iter:
+ free(iter);
+ return err;
+}
+
+static enum btrfs_util_error snapshot_subvolume_children(int fd, int parent_fd,
+ const char *name,
+ uint64_t *async_transid)
+{
+ struct btrfs_util_subvolume_iterator *iter;
+ enum btrfs_util_error err;
+ int dstfd;
+
+ dstfd = openat(parent_fd, name, O_RDONLY);
+ if (dstfd == -1)
+ return BTRFS_UTIL_ERROR_OPEN_FAILED;
+
+ err = btrfs_util_create_subvolume_iterator_fd(fd, 0, 0, &iter);
+ if (err)
+ goto out;
+
+ for (;;) {
+ char child_name[BTRFS_SUBVOL_NAME_MAX + 1];
+ char *child_path;
+ int child_fd, new_parent_fd;
+ uint64_t tmp_transid;
+
+ err = btrfs_util_subvolume_iterator_next(iter, &child_path,
+ NULL);
+ if (err) {
+ if (err == BTRFS_UTIL_ERROR_STOP_ITERATION)
+ err = BTRFS_UTIL_OK;
+ break;
+ }
+
+ /* Remove the placeholder directory. */
+ if (unlinkat(dstfd, child_path, AT_REMOVEDIR) == -1) {
+ free(child_path);
+ err = BTRFS_UTIL_ERROR_RMDIR_FAILED;
+ break;
+ }
+
+ child_fd = openat(fd, child_path, O_RDONLY);
+ if (child_fd == -1) {
+ free(child_path);
+ err = BTRFS_UTIL_ERROR_OPEN_FAILED;
+ break;
+ }
+
+ err = openat_parent_and_name(dstfd, child_path, child_name,
+ sizeof(child_name),
+ &new_parent_fd);
+ free(child_path);
+ if (err) {
+ SAVE_ERRNO_AND_CLOSE(child_fd);
+ break;
+ }
+
+ err = btrfs_util_create_snapshot_fd2(child_fd, new_parent_fd,
+ child_name, 0,
+ async_transid ? &tmp_transid : NULL,
+ NULL);
+ SAVE_ERRNO_AND_CLOSE(child_fd);
+ SAVE_ERRNO_AND_CLOSE(new_parent_fd);
+ if (err)
+ break;
+ if (async_transid && tmp_transid > *async_transid)
+ *async_transid = tmp_transid;
+ }
+
+ btrfs_util_destroy_subvolume_iterator(iter);
+out:
+ SAVE_ERRNO_AND_CLOSE(dstfd);
+ return err;
+}
+
+PUBLIC enum btrfs_util_error btrfs_util_create_snapshot(const char *source,
+ const char *path,
+ int flags,
+ uint64_t *async_transid,
+ struct btrfs_util_qgroup_inherit *qgroup_inherit)
+{
+ enum btrfs_util_error err;
+ int fd;
+
+ fd = open(source, O_RDONLY);
+ if (fd == -1)
+ return BTRFS_UTIL_ERROR_OPEN_FAILED;
+
+ err = btrfs_util_create_snapshot_fd(fd, path, flags, async_transid,
+ qgroup_inherit);
+ SAVE_ERRNO_AND_CLOSE(fd);
+ return err;
+}
+
+PUBLIC enum btrfs_util_error btrfs_util_create_snapshot_fd(int fd,
+ const char *path,
+ int flags,
+ uint64_t *async_transid,
+ struct btrfs_util_qgroup_inherit *qgroup_inherit)
+{
+ char name[BTRFS_SUBVOL_NAME_MAX + 1];
+ enum btrfs_util_error err;
+ int parent_fd;
+
+ err = openat_parent_and_name(AT_FDCWD, path, name, sizeof(name),
+ &parent_fd);
+ if (err)
+ return err;
+
+ err = btrfs_util_create_snapshot_fd2(fd, parent_fd, name, flags,
+ async_transid, qgroup_inherit);
+ SAVE_ERRNO_AND_CLOSE(parent_fd);
+ return err;
+}
+
+PUBLIC enum btrfs_util_error btrfs_util_create_snapshot_fd2(int fd,
+ int parent_fd,
+ const char *name,
+ int flags,
+ uint64_t *async_transid,
+ struct btrfs_util_qgroup_inherit *qgroup_inherit)
+{
+ struct btrfs_ioctl_vol_args_v2 args = {.fd = fd};
+ enum btrfs_util_error err;
+ size_t len;
+ int ret;
+
+ if ((flags & ~BTRFS_UTIL_CREATE_SNAPSHOT_MASK) ||
+ ((flags & BTRFS_UTIL_CREATE_SNAPSHOT_READ_ONLY) &&
+ (flags & BTRFS_UTIL_CREATE_SNAPSHOT_RECURSIVE))) {
+ errno = EINVAL;
+ return BTRFS_UTIL_ERROR_INVALID_ARGUMENT;
+ }
+
+ if (flags & BTRFS_UTIL_CREATE_SNAPSHOT_READ_ONLY)
+ args.flags |= BTRFS_SUBVOL_RDONLY;
+ if (async_transid)
+ args.flags |= BTRFS_SUBVOL_CREATE_ASYNC;
+ if (qgroup_inherit) {
+ args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
+ args.qgroup_inherit = (struct btrfs_qgroup_inherit *)qgroup_inherit;
+ args.size = (sizeof(*args.qgroup_inherit) +
+ args.qgroup_inherit->num_qgroups *
+ sizeof(args.qgroup_inherit->qgroups[0]));
+ }
+
+ len = strlen(name);
+ if (len >= sizeof(args.name)) {
+ errno = ENAMETOOLONG;
+ return BTRFS_UTIL_ERROR_INVALID_ARGUMENT;
+ }
+ memcpy(args.name, name, len);
+ args.name[len] = '\0';
+
+ ret = ioctl(parent_fd, BTRFS_IOC_SNAP_CREATE_V2, &args);
+ if (ret == -1)
+ return BTRFS_UTIL_ERROR_SUBVOL_CREATE_FAILED;
+
+ if (async_transid)
+ *async_transid = args.transid;
+
+ if (flags & BTRFS_UTIL_CREATE_SNAPSHOT_RECURSIVE) {
+ err = snapshot_subvolume_children(fd, parent_fd, name,
+ async_transid);
+ if (err)
+ return err;
+ }
+
+ return BTRFS_UTIL_OK;
+}
+
+static enum btrfs_util_error delete_subvolume_children(int parent_fd,
+ const char *name)
+{
+ struct btrfs_util_subvolume_iterator *iter;
+ enum btrfs_util_error err;
+ int fd;
+
+ fd = openat(parent_fd, name, O_RDONLY);
+ if (fd == -1)
+ return BTRFS_UTIL_ERROR_OPEN_FAILED;
+
+ err = btrfs_util_create_subvolume_iterator_fd(fd, 0,
+ BTRFS_UTIL_SUBVOLUME_ITERATOR_POST_ORDER,
+ &iter);
+ if (err)
+ goto out;
+
+ for (;;) {
+ char child_name[BTRFS_PATH_NAME_MAX + 1];
+ char *child_path;
+ int child_parent_fd;
+
+ err = btrfs_util_subvolume_iterator_next(iter, &child_path,
+ NULL);
+ if (err) {
+ if (err == BTRFS_UTIL_ERROR_STOP_ITERATION)
+ err = BTRFS_UTIL_OK;
+ break;
+ }
+
+ err = openat_parent_and_name(fd, child_path, child_name,
+ sizeof(child_name),
+ &child_parent_fd);
+ free(child_path);
+ if (err)
+ break;
+
+ err = btrfs_util_delete_subvolume_fd(child_parent_fd,
+ child_name, 0);
+ SAVE_ERRNO_AND_CLOSE(child_parent_fd);
+ if (err)
+ break;
+ }
+
+ btrfs_util_destroy_subvolume_iterator(iter);
+out:
+ SAVE_ERRNO_AND_CLOSE(fd);
+ return err;
+}
+
+PUBLIC enum btrfs_util_error btrfs_util_delete_subvolume(const char *path,
+ int flags)
+{
+ char name[BTRFS_PATH_NAME_MAX + 1];
+ enum btrfs_util_error err;
+ int parent_fd;
+
+ err = openat_parent_and_name(AT_FDCWD, path, name, sizeof(name),
+ &parent_fd);
+ if (err)
+ return err;
+
+ err = btrfs_util_delete_subvolume_fd(parent_fd, name, flags);
+ SAVE_ERRNO_AND_CLOSE(parent_fd);
+ return err;
+}
+
+PUBLIC enum btrfs_util_error btrfs_util_delete_subvolume_fd(int parent_fd,
+ const char *name,
+ int flags)
+{
+ struct btrfs_ioctl_vol_args args = {};
+ enum btrfs_util_error err;
+ size_t len;
+ int ret;
+
+ if (flags & ~BTRFS_UTIL_DELETE_SUBVOLUME_MASK) {
+ errno = EINVAL;
+ return BTRFS_UTIL_ERROR_INVALID_ARGUMENT;
+ }
+
+ if (flags & BTRFS_UTIL_DELETE_SUBVOLUME_RECURSIVE) {
+ err = delete_subvolume_children(parent_fd, name);
+ if (err)
+ return err;
+ }
+
+ len = strlen(name);
+ if (len >= sizeof(args.name)) {
+ errno = ENAMETOOLONG;
+ return BTRFS_UTIL_ERROR_INVALID_ARGUMENT;
+ }
+ memcpy(args.name, name, len);
+ args.name[len] = '\0';
+
+ ret = ioctl(parent_fd, BTRFS_IOC_SNAP_DESTROY, &args);
+ if (ret == -1)
+ return BTRFS_UTIL_ERROR_SNAP_DESTROY_FAILED;
+
+ return BTRFS_UTIL_OK;
+}
+
+PUBLIC void btrfs_util_destroy_subvolume_iterator(struct btrfs_util_subvolume_iterator *iter)
+{
+ if (iter) {
+ free(iter->cur_path);
+ free(iter->search_stack);
+ if (iter->flags & BTRFS_UTIL_SUBVOLUME_ITERATOR_CLOSE_FD)
+ SAVE_ERRNO_AND_CLOSE(iter->fd);
+ free(iter);
+ }
+}
+
+PUBLIC int btrfs_util_subvolume_iterator_fd(const struct btrfs_util_subvolume_iterator *iter)
+{
+ return iter->fd;
+}
+
+static struct search_stack_entry *top_search_stack_entry(struct btrfs_util_subvolume_iterator *iter)
+{
+ return &iter->search_stack[iter->search_stack_len - 1];
+}
+
+static enum btrfs_util_error build_subvol_path(struct btrfs_util_subvolume_iterator *iter,
+ const struct btrfs_ioctl_search_header *header,
+ const struct btrfs_root_ref *ref,
+ const char *name,
+ size_t *path_len_ret)
+{
+ struct btrfs_ioctl_ino_lookup_args lookup = {
+ .treeid = header->objectid,
+ .objectid = le64_to_cpu(ref->dirid),
+ };
+ struct search_stack_entry *top = top_search_stack_entry(iter);
+ size_t dir_len, name_len, path_len;
+ char *p;
+ int ret;
+
+ ret = ioctl(iter->fd, BTRFS_IOC_INO_LOOKUP, &lookup);
+ if (ret == -1)
+ return BTRFS_UTIL_ERROR_INO_LOOKUP_FAILED;
+
+ dir_len = strlen(lookup.name);
+ name_len = le16_to_cpu(ref->name_len);
+
+ path_len = top->path_len;
+ /*
+ * We need a joining slash if we have a current path and a subdirectory.
+ */
+ if (top->path_len && dir_len)
+ path_len++;
+ path_len += dir_len;
+ /*
+ * We need another joining slash if we have a current path and a name,
+ * but not if we have a subdirectory, because the lookup ioctl includes
+ * a trailing slash.
+ */
+ if (top->path_len && !dir_len && name_len)
+ path_len++;
+ path_len += name_len;
+
+ if (path_len > iter->cur_path_capacity) {
+ char *tmp = realloc(iter->cur_path, path_len);
+
+ if (!tmp)
+ return BTRFS_UTIL_ERROR_NO_MEMORY;
+ iter->cur_path = tmp;
+ iter->cur_path_capacity = path_len;
+ }
+
+ p = iter->cur_path + top->path_len;
+ if (top->path_len && dir_len)
+ *p++ = '/';
+ memcpy(p, lookup.name, dir_len);
+ p += dir_len;
+ if (top->path_len && !dir_len && name_len)
+ *p++ = '/';
+ memcpy(p, name, name_len);
+ p += name_len;
+
+ *path_len_ret = path_len;
+
+ return BTRFS_UTIL_OK;
+}
+
+PUBLIC enum btrfs_util_error btrfs_util_subvolume_iterator_next(struct btrfs_util_subvolume_iterator *iter,
+ char **path_ret,
+ uint64_t *id_ret)
+{
+ struct search_stack_entry *top;
+ const struct btrfs_ioctl_search_header *header;
+ const struct btrfs_root_ref *ref;
+ const char *name;
+ enum btrfs_util_error err;
+ size_t path_len;
+ int ret;
+
+ for (;;) {
+ for (;;) {
+ if (iter->search_stack_len == 0)
+ return BTRFS_UTIL_ERROR_STOP_ITERATION;
+
+ top = top_search_stack_entry(iter);
+ if (top->items_pos < top->search.key.nr_items) {
+ break;
+ } else {
+ top->search.key.nr_items = 4096;
+ ret = ioctl(iter->fd, BTRFS_IOC_TREE_SEARCH, &top->search);
+ if (ret == -1)
+ return BTRFS_UTIL_ERROR_SEARCH_FAILED;
+ top->items_pos = 0;
+ top->buf_off = 0;
+
+ if (top->search.key.nr_items == 0) {
+ iter->search_stack_len--;
+ if ((iter->flags & BTRFS_UTIL_SUBVOLUME_ITERATOR_POST_ORDER) &&
+ iter->search_stack_len)
+ goto out;
+ }
+ }
+ }
+
+ header = (struct btrfs_ioctl_search_header *)(top->search.buf + top->buf_off);
+
+ top->items_pos++;
+ top->buf_off += sizeof(*header) + header->len;
+ top->search.key.min_offset = header->offset + 1;
+
+ /* This shouldn't happen, but handle it just in case. */
+ if (header->type != BTRFS_ROOT_REF_KEY)
+ continue;
+
+ ref = (struct btrfs_root_ref *)(header + 1);
+ name = (const char *)(ref + 1);
+ err = build_subvol_path(iter, header, ref, name, &path_len);
+ if (err)
+ return err;
+
+ err = append_to_search_stack(iter, header->offset, path_len);
+ if (err)
+ return err;
+
+ if (!(iter->flags & BTRFS_UTIL_SUBVOLUME_ITERATOR_POST_ORDER)) {
+ top = top_search_stack_entry(iter);
+ goto out;
+ }
+ }
+
+out:
+ if (path_ret) {
+ *path_ret = malloc(top->path_len + 1);
+ if (!*path_ret)
+ return BTRFS_UTIL_ERROR_NO_MEMORY;
+ memcpy(*path_ret, iter->cur_path, top->path_len);
+ (*path_ret)[top->path_len] = '\0';
+ }
+ if (id_ret)
+ *id_ret = top->search.key.min_objectid;
+ return BTRFS_UTIL_OK;
+}
+
+PUBLIC enum btrfs_util_error btrfs_util_subvolume_iterator_next_info(struct btrfs_util_subvolume_iterator *iter,
+ char **path_ret,
+ struct btrfs_util_subvolume_info *subvol)
+{
+ enum btrfs_util_error err;
+ uint64_t id;
+
+ err = btrfs_util_subvolume_iterator_next(iter, path_ret, &id);
+ if (err)
+ return err;
+
+ return btrfs_util_subvolume_info_fd(iter->fd, id, subvol);
+}
+
+PUBLIC enum btrfs_util_error btrfs_util_deleted_subvolumes(const char *path,
+ uint64_t **ids,
+ size_t *n)
+{
+ enum btrfs_util_error err;
+ int fd;
+
+ fd = open(path, O_RDONLY);
+ if (fd == -1)
+ return BTRFS_UTIL_ERROR_OPEN_FAILED;
+
+ err = btrfs_util_deleted_subvolumes_fd(fd, ids, n);
+ SAVE_ERRNO_AND_CLOSE(fd);
+ return err;
+}
+
+PUBLIC enum btrfs_util_error btrfs_util_deleted_subvolumes_fd(int fd,
+ uint64_t **ids,
+ size_t *n)
+{
+ size_t capacity = 0;
+ struct btrfs_ioctl_search_args search = {
+ .key = {
+ .tree_id = BTRFS_ROOT_TREE_OBJECTID,
+ .min_objectid = BTRFS_ORPHAN_OBJECTID,
+ .max_objectid = BTRFS_ORPHAN_OBJECTID,
+ .min_type = BTRFS_ORPHAN_ITEM_KEY,
+ .max_type = BTRFS_ORPHAN_ITEM_KEY,
+ .min_offset = 0,
+ .max_offset = UINT64_MAX,
+ .min_transid = 0,
+ .max_transid = UINT64_MAX,
+ .nr_items = 0,
+ },
+ };
+ enum btrfs_util_error err;
+ size_t items_pos = 0, buf_off = 0;
+ int ret;
+
+ *ids = NULL;
+ *n = 0;
+ for (;;) {
+ const struct btrfs_ioctl_search_header *header;
+
+ if (items_pos >= search.key.nr_items) {
+ search.key.nr_items = 4096;
+ ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &search);
+ if (ret == -1) {
+ err = BTRFS_UTIL_ERROR_SEARCH_FAILED;
+ goto out;
+ }
+ items_pos = 0;
+ buf_off = 0;
+
+ if (search.key.nr_items == 0)
+ break;
+ }
+
+ header = (struct btrfs_ioctl_search_header *)(search.buf + buf_off);
+
+ /*
+ * The orphan item might be for a free space cache inode, so
+ * check if there's a matching root item.
+ */
+ err = btrfs_util_subvolume_info_fd(fd, header->offset, NULL);
+ if (!err) {
+ if (*n >= capacity) {
+ size_t new_capacity;
+ uint64_t *new_ids;
+
+ new_capacity = capacity ? capacity * 2 : 1;
+ new_ids = reallocarray(*ids, new_capacity,
+ sizeof(**ids));
+ if (!new_ids) {
+ err = BTRFS_UTIL_ERROR_NO_MEMORY;
+ goto out;
+ }
+
+ *ids = new_ids;
+ capacity = new_capacity;
+ }
+ (*ids)[(*n)++] = header->offset;
+ } else if (err != BTRFS_UTIL_ERROR_SUBVOLUME_NOT_FOUND) {
+ goto out;
+ }
+
+ items_pos++;
+ buf_off += sizeof(*header) + header->len;
+ search.key.min_offset = header->offset + 1;
+ }
+
+ err = BTRFS_UTIL_OK;
+out:
+ if (err) {
+ free(*ids);
+ *ids = NULL;
+ *n = 0;
+ }
+ return err;
+}
diff --git a/messages.h b/messages.h
index 4999c7b9..2b97aa08 100644
--- a/messages.h
+++ b/messages.h
@@ -54,6 +54,19 @@
DO_ABORT_ON_ERROR; \
} while (0)
+#define error_btrfs_util(err) \
+ do { \
+ const char *errno_str = strerror(errno); \
+ const char *lib_str = btrfs_util_strerror(err); \
+ PRINT_TRACE_ON_ERROR; \
+ PRINT_VERBOSE_ERROR; \
+ if (lib_str && strcmp(errno_str, lib_str) != 0) \
+ __btrfs_error("%s: %s", lib_str, errno_str); \
+ else \
+ __btrfs_error("%s", errno_str); \
+ DO_ABORT_ON_ERROR; \
+ } while (0)
+
#define warning(fmt, ...) \
do { \
PRINT_TRACE_ON_ERROR; \
diff --git a/mkfs/common.c b/mkfs/common.c
index 16916ca2..3a80050c 100644
--- a/mkfs/common.c
+++ b/mkfs/common.c
@@ -44,6 +44,7 @@ static int btrfs_create_tree_root(int fd, struct btrfs_mkfs_config *cfg,
u32 itemoff;
int ret = 0;
int blk;
+ u8 uuid[BTRFS_UUID_SIZE];
memset(buf->data + sizeof(struct btrfs_header), 0,
cfg->nodesize - sizeof(struct btrfs_header));
@@ -77,6 +78,19 @@ static int btrfs_create_tree_root(int fd, struct btrfs_mkfs_config *cfg,
btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff);
btrfs_set_item_size(buf, btrfs_item_nr(nritems),
sizeof(root_item));
+ if (blk == MKFS_FS_TREE) {
+ time_t now = time(NULL);
+
+ uuid_generate(uuid);
+ memcpy(root_item.uuid, uuid, BTRFS_UUID_SIZE);
+ btrfs_set_stack_timespec_sec(&root_item.otime, now);
+ btrfs_set_stack_timespec_sec(&root_item.ctime, now);
+ } else {
+ memset(uuid, 0, BTRFS_UUID_SIZE);
+ memcpy(root_item.uuid, uuid, BTRFS_UUID_SIZE);
+ btrfs_set_stack_timespec_sec(&root_item.otime, 0);
+ btrfs_set_stack_timespec_sec(&root_item.ctime, 0);
+ }
write_extent_buffer(buf, &root_item,
btrfs_item_ptr_offset(buf, nritems),
sizeof(root_item));
@@ -85,7 +99,7 @@ static int btrfs_create_tree_root(int fd, struct btrfs_mkfs_config *cfg,
}
/* generate checksum */
- csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
+ csum_tree_block_size(buf, btrfs_csum_sizes[BTRFS_CSUM_TYPE_CRC32], 0);
/* write back root tree */
ret = pwrite(fd, buf->data, cfg->nodesize, cfg->blocks[MKFS_ROOT_TREE]);
@@ -175,7 +189,7 @@ int make_btrfs(int fd, struct btrfs_mkfs_config *cfg)
btrfs_set_super_bytenr(&super, cfg->blocks[MKFS_SUPER_BLOCK]);
btrfs_set_super_num_devices(&super, 1);
- btrfs_set_super_magic(&super, BTRFS_MAGIC_PARTIAL);
+ btrfs_set_super_magic(&super, BTRFS_MAGIC_TEMPORARY);
btrfs_set_super_generation(&super, 1);
btrfs_set_super_root(&super, cfg->blocks[MKFS_ROOT_TREE]);
btrfs_set_super_chunk_root(&super, cfg->blocks[MKFS_CHUNK_TREE]);
@@ -276,7 +290,7 @@ int make_btrfs(int fd, struct btrfs_mkfs_config *cfg)
btrfs_set_header_bytenr(buf, cfg->blocks[MKFS_EXTENT_TREE]);
btrfs_set_header_owner(buf, BTRFS_EXTENT_TREE_OBJECTID);
btrfs_set_header_nritems(buf, nritems);
- csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
+ csum_tree_block_size(buf, btrfs_csum_sizes[BTRFS_CSUM_TYPE_CRC32], 0);
ret = pwrite(fd, buf->data, cfg->nodesize, cfg->blocks[MKFS_EXTENT_TREE]);
if (ret != cfg->nodesize) {
ret = (ret < 0 ? -errno : -EIO);
@@ -364,7 +378,7 @@ int make_btrfs(int fd, struct btrfs_mkfs_config *cfg)
btrfs_set_header_bytenr(buf, cfg->blocks[MKFS_CHUNK_TREE]);
btrfs_set_header_owner(buf, BTRFS_CHUNK_TREE_OBJECTID);
btrfs_set_header_nritems(buf, nritems);
- csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
+ csum_tree_block_size(buf, btrfs_csum_sizes[BTRFS_CSUM_TYPE_CRC32], 0);
ret = pwrite(fd, buf->data, cfg->nodesize, cfg->blocks[MKFS_CHUNK_TREE]);
if (ret != cfg->nodesize) {
ret = (ret < 0 ? -errno : -EIO);
@@ -404,7 +418,7 @@ int make_btrfs(int fd, struct btrfs_mkfs_config *cfg)
btrfs_set_header_bytenr(buf, cfg->blocks[MKFS_DEV_TREE]);
btrfs_set_header_owner(buf, BTRFS_DEV_TREE_OBJECTID);
btrfs_set_header_nritems(buf, nritems);
- csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
+ csum_tree_block_size(buf, btrfs_csum_sizes[BTRFS_CSUM_TYPE_CRC32], 0);
ret = pwrite(fd, buf->data, cfg->nodesize, cfg->blocks[MKFS_DEV_TREE]);
if (ret != cfg->nodesize) {
ret = (ret < 0 ? -errno : -EIO);
@@ -417,7 +431,7 @@ int make_btrfs(int fd, struct btrfs_mkfs_config *cfg)
btrfs_set_header_bytenr(buf, cfg->blocks[MKFS_FS_TREE]);
btrfs_set_header_owner(buf, BTRFS_FS_TREE_OBJECTID);
btrfs_set_header_nritems(buf, 0);
- csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
+ csum_tree_block_size(buf, btrfs_csum_sizes[BTRFS_CSUM_TYPE_CRC32], 0);
ret = pwrite(fd, buf->data, cfg->nodesize, cfg->blocks[MKFS_FS_TREE]);
if (ret != cfg->nodesize) {
ret = (ret < 0 ? -errno : -EIO);
@@ -429,7 +443,7 @@ int make_btrfs(int fd, struct btrfs_mkfs_config *cfg)
btrfs_set_header_bytenr(buf, cfg->blocks[MKFS_CSUM_TREE]);
btrfs_set_header_owner(buf, BTRFS_CSUM_TREE_OBJECTID);
btrfs_set_header_nritems(buf, 0);
- csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
+ csum_tree_block_size(buf, btrfs_csum_sizes[BTRFS_CSUM_TYPE_CRC32], 0);
ret = pwrite(fd, buf->data, cfg->nodesize, cfg->blocks[MKFS_CSUM_TREE]);
if (ret != cfg->nodesize) {
ret = (ret < 0 ? -errno : -EIO);
@@ -440,7 +454,7 @@ int make_btrfs(int fd, struct btrfs_mkfs_config *cfg)
memset(buf->data, 0, BTRFS_SUPER_INFO_SIZE);
memcpy(buf->data, &super, sizeof(super));
buf->len = BTRFS_SUPER_INFO_SIZE;
- csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
+ csum_tree_block_size(buf, btrfs_csum_sizes[BTRFS_CSUM_TYPE_CRC32], 0);
ret = pwrite(fd, buf->data, BTRFS_SUPER_INFO_SIZE,
cfg->blocks[MKFS_SUPER_BLOCK]);
if (ret != BTRFS_SUPER_INFO_SIZE) {
diff --git a/mkfs/main.c b/mkfs/main.c
index 5a717f70..9bfddf30 100644
--- a/mkfs/main.c
+++ b/mkfs/main.c
@@ -315,6 +315,7 @@ static int create_tree(struct btrfs_trans_handle *trans,
struct btrfs_key location;
struct btrfs_root_item root_item;
struct extent_buffer *tmp;
+ u8 uuid[BTRFS_UUID_SIZE] = {0};
int ret;
ret = btrfs_copy_root(trans, root, root->node, &tmp, objectid);
@@ -325,6 +326,10 @@ static int create_tree(struct btrfs_trans_handle *trans,
btrfs_set_root_bytenr(&root_item, tmp->start);
btrfs_set_root_level(&root_item, btrfs_header_level(tmp));
btrfs_set_root_generation(&root_item, trans->transid);
+ /* clear uuid and o/ctime of source tree */
+ memcpy(root_item.uuid, uuid, BTRFS_UUID_SIZE);
+ btrfs_set_stack_timespec_sec(&root_item.otime, 0);
+ btrfs_set_stack_timespec_sec(&root_item.ctime, 0);
free_extent_buffer(tmp);
location.objectid = objectid;
@@ -1094,7 +1099,7 @@ int main(int argc, char **argv)
}
fs_info = open_ctree_fs_info(file, 0, 0, 0,
- OPEN_CTREE_WRITES | OPEN_CTREE_FS_PARTIAL);
+ OPEN_CTREE_WRITES | OPEN_CTREE_TEMPORARY_SUPER);
if (!fs_info) {
error("open ctree failed");
goto error;
@@ -1203,6 +1208,11 @@ raid_groups:
goto out;
}
+ ret = create_tree(trans, root, BTRFS_UUID_TREE_OBJECTID);
+ if (ret)
+ warning(
+ "unable to create uuid tree, will be created after mount: %d", ret);
+
ret = btrfs_commit_transaction(trans, root);
if (ret) {
error("unable to commit transaction: %d", ret);
@@ -1285,6 +1295,12 @@ out:
}
}
+ if (!ret && close_ret) {
+ ret = close_ret;
+ error("failed to close ctree, the filesystem may be inconsistent: %d",
+ ret);
+ }
+
btrfs_close_all_devices();
free(label);
diff --git a/mkfs/rootdir.c b/mkfs/rootdir.c
index e06b65ac..ff00bb0f 100644
--- a/mkfs/rootdir.c
+++ b/mkfs/rootdir.c
@@ -139,7 +139,8 @@ static int fill_inode_item(struct btrfs_trans_handle *trans,
}
if (S_ISREG(src->st_mode)) {
btrfs_set_stack_inode_size(dst, (u64)src->st_size);
- if (src->st_size <= BTRFS_MAX_INLINE_DATA_SIZE(root->fs_info))
+ if (src->st_size <= BTRFS_MAX_INLINE_DATA_SIZE(root->fs_info) &&
+ src->st_size < sectorsize)
btrfs_set_stack_inode_nbytes(dst, src->st_size);
else {
blocks = src->st_size / sectorsize;
@@ -248,7 +249,7 @@ static int add_xattr_item(struct btrfs_trans_handle *trans,
cur_name_len = strlen(cur_name);
next_location += cur_name_len + 1;
- ret = getxattr(file_name, cur_name, cur_value, XATTR_SIZE_MAX);
+ ret = lgetxattr(file_name, cur_name, cur_value, XATTR_SIZE_MAX);
if (ret < 0) {
if (errno == ENOTSUP)
return 0;
@@ -327,7 +328,8 @@ static int add_file_items(struct btrfs_trans_handle *trans,
if (st->st_size % sectorsize)
blocks += 1;
- if (st->st_size <= BTRFS_MAX_INLINE_DATA_SIZE(root->fs_info)) {
+ if (st->st_size <= BTRFS_MAX_INLINE_DATA_SIZE(root->fs_info) &&
+ st->st_size < sectorsize) {
char *buffer = malloc(st->st_size);
if (!buffer) {
@@ -694,7 +696,7 @@ out:
}
static int ftw_add_entry_size(const char *fpath, const struct stat *st,
- int type)
+ int type, struct FTW *ftwbuf)
{
/*
* Failed to read the directory, mostly due to EPERM. Abort ASAP, so
@@ -729,7 +731,12 @@ u64 btrfs_mkfs_size_dir(const char *dir_name, u32 sectorsize, u64 min_dev_size,
fs_block_size = sectorsize;
ftw_data_size = 0;
ftw_meta_nr_inode = 0;
- ret = ftw(dir_name, ftw_add_entry_size, 10);
+
+ /*
+ * Symbolic link is not followed when creating files, so no need to
+ * follow them here.
+ */
+ ret = nftw(dir_name, ftw_add_entry_size, 10, FTW_PHYS);
if (ret < 0) {
error("ftw subdir walk of %s failed: %s", dir_name,
strerror(errno));
diff --git a/print-tree.c b/print-tree.c
index 45350fea..a1a7954a 100644
--- a/print-tree.c
+++ b/print-tree.c
@@ -834,7 +834,16 @@ void btrfs_print_key(struct btrfs_disk_key *disk_key)
*/
case BTRFS_ROOT_ITEM_KEY:
printf(" ");
- print_objectid(stdout, offset, type);
+ /*
+ * Normally offset of ROOT_ITEM should present the generation
+ * of creation time of the root.
+ * However if this is reloc tree, offset is the subvolume
+ * id of its source. Here we do extra check on this.
+ */
+ if (objectid == BTRFS_TREE_RELOC_OBJECTID)
+ print_objectid(stdout, offset, type);
+ else
+ printf("%lld", offset);
printf(")");
break;
default:
@@ -1164,8 +1173,9 @@ static void header_flags_to_str(u64 flags, char *ret)
}
}
-void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *eb)
+void btrfs_print_leaf(struct extent_buffer *eb)
{
+ struct btrfs_fs_info *fs_info = eb->fs_info;
struct btrfs_item *item;
struct btrfs_disk_key disk_key;
char flags_str[128];
@@ -1179,11 +1189,12 @@ void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *eb)
header_flags_to_str(flags, flags_str);
nr = btrfs_header_nritems(eb);
- printf("leaf %llu items %d free space %d generation %llu owner %llu\n",
+ printf("leaf %llu items %d free space %d generation %llu owner ",
(unsigned long long)btrfs_header_bytenr(eb), nr,
- btrfs_leaf_free_space(root, eb),
- (unsigned long long)btrfs_header_generation(eb),
- (unsigned long long)btrfs_header_owner(eb));
+ btrfs_leaf_free_space(fs_info, eb),
+ (unsigned long long)btrfs_header_generation(eb));
+ print_objectid(stdout, btrfs_header_owner(eb), 0);
+ printf("\n");
printf("leaf %llu flags 0x%llx(%s) backref revision %d\n",
btrfs_header_bytenr(eb), flags, flags_str, backref_rev);
print_uuids(eb);
@@ -1280,7 +1291,7 @@ void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *eb)
printf("\t\tcsum item\n");
break;
case BTRFS_EXTENT_CSUM_KEY:
- print_extent_csum(eb, root->fs_info, item_size,
+ print_extent_csum(eb, fs_info, item_size,
offset);
break;
case BTRFS_EXTENT_DATA_KEY:
@@ -1341,10 +1352,11 @@ void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *eb)
}
}
-void btrfs_print_tree(struct btrfs_root *root, struct extent_buffer *eb, int follow)
+void btrfs_print_tree(struct extent_buffer *eb, int follow)
{
u32 i;
u32 nr;
+ struct btrfs_fs_info *fs_info = eb->fs_info;
struct btrfs_disk_key disk_key;
struct btrfs_key key;
struct extent_buffer *next;
@@ -1353,15 +1365,16 @@ void btrfs_print_tree(struct btrfs_root *root, struct extent_buffer *eb, int fol
return;
nr = btrfs_header_nritems(eb);
if (btrfs_is_leaf(eb)) {
- btrfs_print_leaf(root, eb);
+ btrfs_print_leaf(eb);
return;
}
- printf("node %llu level %d items %d free %u generation %llu owner %llu\n",
+ printf("node %llu level %d items %d free %u generation %llu owner ",
(unsigned long long)eb->start,
btrfs_header_level(eb), nr,
- (u32)BTRFS_NODEPTRS_PER_BLOCK(root->fs_info) - nr,
- (unsigned long long)btrfs_header_generation(eb),
- (unsigned long long)btrfs_header_owner(eb));
+ (u32)BTRFS_NODEPTRS_PER_BLOCK(fs_info) - nr,
+ (unsigned long long)btrfs_header_generation(eb));
+ print_objectid(stdout, btrfs_header_owner(eb), 0);
+ printf("\n");
print_uuids(eb);
fflush(stdout);
for (i = 0; i < nr; i++) {
@@ -1372,7 +1385,7 @@ void btrfs_print_tree(struct btrfs_root *root, struct extent_buffer *eb, int fol
btrfs_print_key(&disk_key);
printf(" block %llu (%llu) gen %llu\n",
(unsigned long long)blocknr,
- (unsigned long long)blocknr / root->fs_info->nodesize,
+ (unsigned long long)blocknr / fs_info->nodesize,
(unsigned long long)btrfs_node_ptr_generation(eb, i));
fflush(stdout);
}
@@ -1380,7 +1393,7 @@ void btrfs_print_tree(struct btrfs_root *root, struct extent_buffer *eb, int fol
return;
for (i = 0; i < nr; i++) {
- next = read_tree_block(root->fs_info,
+ next = read_tree_block(fs_info,
btrfs_node_blockptr(eb, i),
btrfs_node_ptr_generation(eb, i));
if (!extent_buffer_uptodate(next)) {
@@ -1389,26 +1402,22 @@ void btrfs_print_tree(struct btrfs_root *root, struct extent_buffer *eb, int fol
(unsigned long long)btrfs_header_owner(eb));
continue;
}
- if (btrfs_is_leaf(next) && btrfs_header_level(eb) != 1) {
- warning(
- "eb corrupted: item %d eb level %d next level %d, skipping the rest",
- i, btrfs_header_level(next),
- btrfs_header_level(eb));
- goto out;
- }
if (btrfs_header_level(next) != btrfs_header_level(eb) - 1) {
warning(
- "eb corrupted: item %d eb level %d next level %d, skipping the rest",
- i, btrfs_header_level(next),
- btrfs_header_level(eb));
- goto out;
+"eb corrupted: parent bytenr %llu slot %d level %d child bytenr %llu level has %d expect %d, skipping the slot",
+ btrfs_header_bytenr(eb), i,
+ btrfs_header_level(eb),
+ btrfs_header_bytenr(next),
+ btrfs_header_level(next),
+ btrfs_header_level(eb) - 1);
+ free_extent_buffer(next);
+ continue;
}
- btrfs_print_tree(root, next, 1);
+ btrfs_print_tree(next, 1);
free_extent_buffer(next);
}
return;
-out:
free_extent_buffer(next);
}
diff --git a/print-tree.h b/print-tree.h
index df58b846..62667d7f 100644
--- a/print-tree.h
+++ b/print-tree.h
@@ -19,8 +19,8 @@
#ifndef __PRINT_TREE_H__
#define __PRINT_TREE_H__
-void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l);
-void btrfs_print_tree(struct btrfs_root *root, struct extent_buffer *t, int follow);
+void btrfs_print_leaf(struct extent_buffer *l);
+void btrfs_print_tree(struct extent_buffer *t, int follow);
void btrfs_print_key(struct btrfs_disk_key *disk_key);
void print_chunk_item(struct extent_buffer *eb, struct btrfs_chunk *chunk);
void print_extent_item(struct extent_buffer *eb, int slot, int metadata);
diff --git a/props.c b/props.c
index cddbd927..e4edba06 100644
--- a/props.c
+++ b/props.c
@@ -21,6 +21,8 @@
#include <fcntl.h>
#include <unistd.h>
+#include <btrfsutil.h>
+
#include "ctree.h"
#include "commands.h"
#include "utils.h"
@@ -41,56 +43,35 @@ static int prop_read_only(enum prop_object_type type,
const char *name,
const char *value)
{
- int ret = 0;
- int fd = -1;
- u64 flags = 0;
-
- fd = open(object, O_RDONLY);
- if (fd < 0) {
- ret = -errno;
- error("failed to open %s: %s", object, strerror(-ret));
- goto out;
- }
+ enum btrfs_util_error err;
+ bool read_only;
- ret = ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags);
- if (ret < 0) {
- ret = -errno;
- error("failed to get flags for %s: %s", object,
- strerror(-ret));
- goto out;
- }
-
- if (!value) {
- if (flags & BTRFS_SUBVOL_RDONLY)
- fprintf(stdout, "ro=true\n");
- else
- fprintf(stdout, "ro=false\n");
- ret = 0;
- goto out;
- }
+ if (value) {
+ if (!strcmp(value, "true")) {
+ read_only = true;
+ } else if (!strcmp(value, "false")) {
+ read_only = false;
+ } else {
+ error("invalid value for property: %s", value);
+ return -EINVAL;
+ }
- if (!strcmp(value, "true")) {
- flags |= BTRFS_SUBVOL_RDONLY;
- } else if (!strcmp(value, "false")) {
- flags = flags & ~BTRFS_SUBVOL_RDONLY;
+ err = btrfs_util_set_subvolume_read_only(object, read_only);
+ if (err) {
+ error_btrfs_util(err);
+ return -errno;
+ }
} else {
- ret = -EINVAL;
- error("invalid value for property: %s", value);
- goto out;
- }
+ err = btrfs_util_get_subvolume_read_only(object, &read_only);
+ if (err) {
+ error_btrfs_util(err);
+ return -errno;
+ }
- ret = ioctl(fd, BTRFS_IOC_SUBVOL_SETFLAGS, &flags);
- if (ret < 0) {
- ret = -errno;
- error("failed to set flags for %s: %s", object,
- strerror(-ret));
- goto out;
+ printf("ro=%s\n", read_only ? "true" : "false");
}
-out:
- if (fd != -1)
- close(fd);
- return ret;
+ return 0;
}
static int prop_label(enum prop_object_type type,
diff --git a/send-utils.c b/send-utils.c
index b5289e76..3ecbdea6 100644
--- a/send-utils.c
+++ b/send-utils.c
@@ -757,18 +757,6 @@ int path_cat_out(char *out, const char *p1, const char *p2)
return 0;
}
-__attribute__((deprecated))
-char *path_cat(const char *p1, const char *p2)
-{
- int p1_len = strlen(p1);
- int p2_len = strlen(p2);
- char *new = malloc(p1_len + p2_len + 2);
-
- path_cat_out(new, p1, p2);
-
- return new;
-}
-
int path_cat3_out(char *out, const char *p1, const char *p2, const char *p3)
{
int p1_len = strlen(p1);
@@ -788,16 +776,3 @@ int path_cat3_out(char *out, const char *p1, const char *p2, const char *p3)
return 0;
}
-
-__attribute__((deprecated))
-char *path_cat3(const char *p1, const char *p2, const char *p3)
-{
- int p1_len = strlen(p1);
- int p2_len = strlen(p2);
- int p3_len = strlen(p3);
- char *new = malloc(p1_len + p2_len + p3_len + 3);
-
- path_cat3_out(new, p1, p2, p3);
-
- return new;
-}
diff --git a/send-utils.h b/send-utils.h
index e8f86912..5f5e096f 100644
--- a/send-utils.h
+++ b/send-utils.h
@@ -104,15 +104,6 @@ void subvol_uuid_search_add(struct subvol_uuid_search *s,
int btrfs_subvolid_resolve(int fd, char *path, size_t path_len, u64 subvol_id);
-/*
- * DEPRECATED: the functions path_cat and path_cat3 are unsafe and should not
- * be used, use the _out variants and always check the return code.
- */
-__attribute__((deprecated))
-char *path_cat(const char *p1, const char *p2);
-__attribute__((deprecated))
-char *path_cat3(const char *p1, const char *p2, const char *p3);
-
int path_cat_out(char *out, const char *p1, const char *p2);
int path_cat3_out(char *out, const char *p1, const char *p2, const char *p3);
diff --git a/show-blocks b/show-blocks
index 49e1770e..3876b2ac 100755
--- a/show-blocks
+++ b/show-blocks
@@ -108,7 +108,7 @@ def loaddata(fh,delimiter=None, converters=None):
return X
def run_debug_tree(device):
- p = os.popen('btrfs-debug-tree -e ' + device)
+ p = os.popen('btrfs inspect-internal dump-tree -e ' + device)
data = loaddata(p)
return data
diff --git a/tests/README.md b/tests/README.md
index d4b80da1..d14d727c 100644
--- a/tests/README.md
+++ b/tests/README.md
@@ -189,6 +189,27 @@ $ TEST=012\* ./misc-tests.sh # from tests/
7. A commit that fixes a bug should be applied before the test that verifies
the fix. This is to keep the git history bisectable.
+
+### Test images
+
+Most tests should be able to create the test images from scratch, using regular
+commands and file operation. The commands also document the testcase and use
+the teste code and kernel of the environment.
+
+In other cases, a pre-created image may be the right way if the above does not
+work (eg. comparing output, requesting an exact layout or some intermediate
+state that would be hard to achieve otherwise).
+
+* images that don't need data and valid checksums can be created by
+ `btrfs-image`, the image can be compressed by the tool itself (file extension
+ `.img`) or compressed externally (recognized is `.img.xz`)
+
+* raw images that are binary dump of an existing image, created eg. from a
+ sparse file (`.raw` or `.raw.xz`)
+
+Use `xz --best` and try to get the smallest size as the file is stored in git.
+
+
### Crafted/fuzzed images
Images that are created by fuzzing or specially crafted to trigger some error
@@ -232,9 +253,10 @@ The tests assume write acesss to their directories.
* quote all variables by default, any path, even the TOP could need that, and
we use it everywhere
+ * even if the variable is safe, use quotes for consistency and to ease
+ reading the code
* there are exceptions:
* `$SUDO_HELPER` as it might be intentionally unset
- * the variable is obviously set to a value that does not require it
* use `#!/bin/bash` explicitly
* check for all external dependencies (`check_prereq_global`)
* check for internal dependencies (`check_prereq`), though the basic set is
diff --git a/tests/common b/tests/common
index fae30f1d..4b266c5b 100644
--- a/tests/common
+++ b/tests/common
@@ -54,6 +54,13 @@ _log()
echo "$*" | tee -a "$RESULTS"
}
+# copy stdout to log and pass to stdout, eg. another stdout consumer, commands
+# should redirect stderr to stdout if this is consmed by further commands
+_log_stdout()
+{
+ tee -a "$RESULTS"
+}
+
_not_run()
{
echo " [NOTRUN] $*"
diff --git a/tests/convert-tests/014-reiserfs-tail-handling/test.sh b/tests/convert-tests/014-reiserfs-tail-handling/test.sh
index 5714dc6c..3be2ed5b 100755
--- a/tests/convert-tests/014-reiserfs-tail-handling/test.sh
+++ b/tests/convert-tests/014-reiserfs-tail-handling/test.sh
@@ -18,10 +18,9 @@ prepare_test_dev
check_prereq btrfs-convert
check_global_prereq md5sum
check_global_prereq mkreiserfs
-check_global_prereq perl
-perl -e "print 'a'x8192;" > input
-perl -e "print 'b'x8192;" > input2
+printf "%0.sa" {1..8192} > input
+printf "%0.sb" {1..8192} > input2
default_mkfs="mkreiserfs -b 4096"
convert_test_preamble '' 'tail conversion test' 16k "$default_mkfs"
diff --git a/tests/convert-tests/016-invalid-large-inline-extent/test.sh b/tests/convert-tests/016-invalid-large-inline-extent/test.sh
new file mode 100755
index 00000000..f37c7c09
--- /dev/null
+++ b/tests/convert-tests/016-invalid-large-inline-extent/test.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+# Check if btrfs-convert refuses to rollback the filesystem, and leave the fs
+# and the convert image untouched
+
+source "$TEST_TOP/common"
+source "$TEST_TOP/common.convert"
+
+setup_root_helper
+prepare_test_dev
+check_prereq btrfs-convert
+check_global_prereq mke2fs
+
+convert_test_prep_fs ext4 mke2fs -t ext4 -b 4096
+
+# Create a 6K file, which should not be inlined
+run_check $SUDO_HELPER dd if=/dev/zero bs=2k count=3 of="$TEST_MNT/file1"
+
+run_check_umount_test_dev
+
+# convert_test_do_convert() will call btrfs check, which should expose any
+# invalid inline extent with too large size
+convert_test_do_convert
diff --git a/tests/fsck-tests/015-tree-reloc-tree/test.sh b/tests/fsck-tests/015-tree-reloc-tree/test.sh
index afad1e8d..5d9d5122 100755
--- a/tests/fsck-tests/015-tree-reloc-tree/test.sh
+++ b/tests/fsck-tests/015-tree-reloc-tree/test.sh
@@ -5,15 +5,12 @@
# Also due to the short life span of reloc tree, save the as dump example for
# later usage.
-source "$TOP/tests/common"
+source "$TEST_TOP/common"
check_prereq btrfs
check_image() {
- local image
-
- image=$1
- run_check "$TOP/btrfs" check "$image"
+ run_check "$TOP/btrfs" check "$1"
}
check_all_images
diff --git a/tests/fsck-tests/020-extent-ref-cases/extent_data_ref.img b/tests/fsck-tests/020-extent-ref-cases/extent_data_ref.img
new file mode 100644
index 00000000..3ab2396b
--- /dev/null
+++ b/tests/fsck-tests/020-extent-ref-cases/extent_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
index 9cf99a51..a1bf75b1 100755
--- a/tests/fsck-tests/020-extent-ref-cases/test.sh
+++ b/tests/fsck-tests/020-extent-ref-cases/test.sh
@@ -20,10 +20,7 @@ source "$TEST_TOP/common"
check_prereq btrfs
check_image() {
- local image
-
- image=$1
- run_check "$TOP/btrfs" check "$image"
+ run_check "$TOP/btrfs" check "$1"
}
check_all_images
diff --git a/tests/fsck-tests/029-valid-orphan-item/orphan_inode.img.xz b/tests/fsck-tests/029-valid-orphan-item/orphan_inode.img.xz
new file mode 100644
index 00000000..26e4cf8a
--- /dev/null
+++ b/tests/fsck-tests/029-valid-orphan-item/orphan_inode.img.xz
Binary files differ
diff --git a/tests/fsck-tests/029-valid-orphan-item/test.sh b/tests/fsck-tests/029-valid-orphan-item/test.sh
new file mode 100755
index 00000000..d8068f63
--- /dev/null
+++ b/tests/fsck-tests/029-valid-orphan-item/test.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+# To check if btrfs check can handle valid orphan items.
+# Orphan item is a marker for deleted inodes that were open at the time of
+# deletion. # Orphan inode/root will is not referenced and will have an orphan
+# item, which should not be reported as error.
+
+source "$TEST_TOP/common"
+
+check_prereq btrfs
+
+check_image() {
+ run_check "$TOP/btrfs" check "$1"
+}
+
+check_all_images
diff --git a/tests/fsck-tests/030-reflinked-prealloc-extents/reflinked-prealloc-extents.img.xz b/tests/fsck-tests/030-reflinked-prealloc-extents/reflinked-prealloc-extents.img.xz
new file mode 100644
index 00000000..8adf0071
--- /dev/null
+++ b/tests/fsck-tests/030-reflinked-prealloc-extents/reflinked-prealloc-extents.img.xz
Binary files differ
diff --git a/tests/fsck-tests/030-reflinked-prealloc-extents/test.sh b/tests/fsck-tests/030-reflinked-prealloc-extents/test.sh
new file mode 100755
index 00000000..63f692bc
--- /dev/null
+++ b/tests/fsck-tests/030-reflinked-prealloc-extents/test.sh
@@ -0,0 +1,42 @@
+#!/bin/bash
+#
+# Verify that a filesystem check operation (fsck) does not report the following
+# scenario as an error:
+#
+# An extent is shared between two inodes, as a result of clone/reflink operation,
+# and for one of the inodes, lets call it inode A, the extent is referenced
+# through a file extent item as a prealloc extent, while for the other inode,
+# call it inode B, the extent is referenced through a regular file extent item,
+# that is, it was written to. The goal of this test is to make sure a filesystem
+# check operation will not report "odd csum items" errors for the prealloc
+# extent at inode A, because this scenario is valid since the extent was written
+# through inode B and therefore it is expected to have checksum items in the
+# filesystem's checksum btree for that shared extent.
+#
+# Such scenario can be created with the following steps for example:
+#
+# mkfs.btrfs -f /dev/sdb
+# mount /dev/sdb /mnt
+#
+# touch /mnt/foo
+# xfs_io -c "falloc 0 256K" /mnt/foo
+# sync
+#
+# xfs_io -c "pwrite -S 0xab 0 256K" /mnt/foo
+# touch /mnt/bar
+# xfs_io -c "reflink /mnt/foo 0 0 256K" /mnt/bar
+# xfs_io -c "fsync" /mnt/bar
+#
+# <power fail>
+# mount /dev/sdb /mnt
+# umount /mnt
+
+source "$TEST_TOP/common"
+
+check_prereq btrfs
+
+check_image() {
+ run_check "$TOP/btrfs" check "$1"
+}
+
+check_all_images
diff --git a/tests/fsck-tests/031-metadatadump-check-data-csum/test.sh b/tests/fsck-tests/031-metadatadump-check-data-csum/test.sh
new file mode 100755
index 00000000..30b0b7a3
--- /dev/null
+++ b/tests/fsck-tests/031-metadatadump-check-data-csum/test.sh
@@ -0,0 +1,30 @@
+#!/bin/bash
+# To check if "btrfs check" can detect metadata dump (restored by btrfs-iamge)
+# and ignore --check-data-csum option
+
+source "$TEST_TOP/common"
+
+check_prereq btrfs
+check_prereq mkfs.btrfs
+check_prereq btrfs-image
+setup_root_helper
+prepare_test_dev
+
+run_check $SUDO_HELPER "$TOP/mkfs.btrfs" -f "$TEST_DEV"
+run_check_mount_test_dev
+
+run_check $SUDO_HELPER dd if=/dev/urandom of="$TEST_MNT/file" bs=4k count=16
+run_check_umount_test_dev
+
+run_check $SUDO_HELPER "$TOP/btrfs-image" "$TEST_DEV" "restored_image"
+
+# use prepare_test_dev() to wipe all existing data on $TEST_DEV
+# so there is no way that restored image could have mathcing data csum
+prepare_test_dev
+
+run_check $SUDO_HELPER "$TOP/btrfs-image" -r "restored_image" "$TEST_DEV"
+
+# Should not report any error
+run_check "$TOP/btrfs" check --check-data-csum "$TEST_DEV"
+
+rm -rf -- "restored_image*"
diff --git a/tests/fuzz-tests/001-simple-check-unmounted/test.sh b/tests/fuzz-tests/001-simple-check-unmounted/test.sh
index 905d37b7..87a63944 100755
--- a/tests/fuzz-tests/001-simple-check-unmounted/test.sh
+++ b/tests/fuzz-tests/001-simple-check-unmounted/test.sh
@@ -9,12 +9,9 @@ check_prereq btrfs
# redefine the one provided by common
check_image() {
- local image
-
- image=$1
- run_mayfail $TOP/btrfs check "$image"
+ run_mayfail "$TOP/btrfs" check "$1"
}
-check_all_images $TEST_TOP/fuzz-tests/images
+check_all_images "$TEST_TOP/fuzz-tests/images"
exit 0
diff --git a/tests/fuzz-tests/002-simple-image/test.sh b/tests/fuzz-tests/002-simple-image/test.sh
index 034fcf46..5dddc9e1 100755
--- a/tests/fuzz-tests/002-simple-image/test.sh
+++ b/tests/fuzz-tests/002-simple-image/test.sh
@@ -9,15 +9,12 @@ check_prereq btrfs-image
# redefine the one provided by common
check_image() {
- local image
-
- image=$1
truncate -s0 target
- run_mayfail $TOP/btrfs-image "$image" target
+ run_mayfail "$TOP/btrfs-image" "$1" target
truncate -s0 target
}
-check_all_images $TEST_TOP/fuzz-tests/images
+check_all_images "$TEST_TOP/fuzz-tests/images"
rm -- target
diff --git a/tests/fuzz-tests/003-multi-check-unmounted/test.sh b/tests/fuzz-tests/003-multi-check-unmounted/test.sh
index 35dfe4fc..3021c3a8 100755
--- a/tests/fuzz-tests/003-multi-check-unmounted/test.sh
+++ b/tests/fuzz-tests/003-multi-check-unmounted/test.sh
@@ -21,6 +21,6 @@ check_image() {
run_mayfail $TOP/btrfs check --repair "$image"
}
-check_all_images $TEST_TOP/fuzz-tests/images
+check_all_images "$TEST_TOP/fuzz-tests/images"
exit 0
diff --git a/tests/fuzz-tests/004-simple-dump-tree/test.sh b/tests/fuzz-tests/004-simple-dump-tree/test.sh
index 6c9e8c45..c09b8478 100755
--- a/tests/fuzz-tests/004-simple-dump-tree/test.sh
+++ b/tests/fuzz-tests/004-simple-dump-tree/test.sh
@@ -7,12 +7,9 @@ check_prereq btrfs
# redefine the one provided by common
check_image() {
- local image
-
- image=$1
- run_mayfail $TOP/btrfs inspect-internal dump-tree "$image"
+ run_mayfail "$TOP/btrfs" inspect-internal dump-tree "$1"
}
-check_all_images $TEST_TOP/fuzz-tests/images
+check_all_images "$TEST_TOP/fuzz-tests/images"
exit 0
diff --git a/tests/fuzz-tests/005-simple-dump-super/test.sh b/tests/fuzz-tests/005-simple-dump-super/test.sh
index 01c7b628..18f71a48 100755
--- a/tests/fuzz-tests/005-simple-dump-super/test.sh
+++ b/tests/fuzz-tests/005-simple-dump-super/test.sh
@@ -10,10 +10,10 @@ check_image() {
local image
image=$1
- run_mayfail $TOP/btrfs inspect-internal dump-super "$image"
- run_mayfail $TOP/btrfs inspect-internal dump-super -Ffa "$image"
+ run_mayfail "$TOP/btrfs" inspect-internal dump-super "$image"
+ run_mayfail "$TOP/btrfs" inspect-internal dump-super -Ffa "$image"
}
-check_all_images $TEST_TOP/fuzz-tests/images
+check_all_images "$TEST_TOP/fuzz-tests/images"
exit 0
diff --git a/tests/fuzz-tests/006-simple-tree-stats/test.sh b/tests/fuzz-tests/006-simple-tree-stats/test.sh
index dbed471a..146afa93 100755
--- a/tests/fuzz-tests/006-simple-tree-stats/test.sh
+++ b/tests/fuzz-tests/006-simple-tree-stats/test.sh
@@ -7,12 +7,9 @@ check_prereq btrfs
# redefine the one provided by common
check_image() {
- local image
-
- image=$1
- run_mayfail $TOP/btrfs inspect-internal tree-stats "$image"
+ run_mayfail "$TOP/btrfs" inspect-internal tree-stats "$1"
}
-check_all_images $TEST_TOP/fuzz-tests/images
+check_all_images "$TEST_TOP/fuzz-tests/images"
exit 0
diff --git a/tests/fuzz-tests/008-simple-chunk-recover/test.sh b/tests/fuzz-tests/008-simple-chunk-recover/test.sh
index 198f88e2..4b27de0c 100755
--- a/tests/fuzz-tests/008-simple-chunk-recover/test.sh
+++ b/tests/fuzz-tests/008-simple-chunk-recover/test.sh
@@ -11,10 +11,10 @@ check_image() {
image=$1
run_check cp "$image" "$image".scratch
- run_mayfail $TOP/btrfs rescue chunk-recover -y -v "$image".scratch
+ run_mayfail "$TOP/btrfs" rescue chunk-recover -y -v "$image".scratch
rm -- "$image".scratch
}
-check_all_images $TEST_TOP/fuzz-tests/images
+check_all_images "$TEST_TOP/fuzz-tests/images"
exit 0
diff --git a/tests/fuzz-tests/009-simple-zero-log/test.sh b/tests/fuzz-tests/009-simple-zero-log/test.sh
index 928c66b1..f78e5b50 100755
--- a/tests/fuzz-tests/009-simple-zero-log/test.sh
+++ b/tests/fuzz-tests/009-simple-zero-log/test.sh
@@ -11,10 +11,10 @@ check_image() {
image=$1
run_check cp "$image" "$image".scratch
- run_mayfail $TOP/btrfs rescue zero-log "$image".scratch
+ run_mayfail "$TOP/btrfs" rescue zero-log "$image".scratch
rm -- "$image".scratch
}
-check_all_images $TEST_TOP/fuzz-tests/images
+check_all_images "$TEST_TOP/fuzz-tests/images"
exit 0
diff --git a/tests/misc-tests.sh b/tests/misc-tests.sh
index 94703a3e..dad397ec 100755
--- a/tests/misc-tests.sh
+++ b/tests/misc-tests.sh
@@ -46,7 +46,6 @@ check_prereq btrfs-corrupt-block
check_prereq btrfs-image
check_prereq btrfstune
check_prereq btrfs
-check_prereq btrfs-zero-log
check_prereq btrfs-find-root
check_prereq btrfs-select-super
check_kernel_support
diff --git a/tests/misc-tests/001-btrfstune-features/test.sh b/tests/misc-tests/001-btrfstune-features/test.sh
index 718e4b08..e3da3949 100755
--- a/tests/misc-tests/001-btrfstune-features/test.sh
+++ b/tests/misc-tests/001-btrfstune-features/test.sh
@@ -21,21 +21,22 @@ test_feature()
local tuneopt
local sbflag
- mkfsfeatures=${1:+-O ^$1}
+ mkfsfeatures=$1
tuneopt="$2"
sbflag="$3"
- run_check $SUDO_HELPER $TOP/mkfs.btrfs -f $mkfsfeatures $TEST_DEV
- if run_check_stdout $TOP/btrfs inspect-internal dump-super $TEST_DEV | \
+ run_check $SUDO_HELPER "$TOP/mkfs.btrfs" -f \
+ ${mkfsfeatures:+-O ^"$mkfsfeatures"} "$TEST_DEV"
+ if run_check_stdout "$TOP/btrfs" inspect-internal dump-super "$TEST_DEV" | \
grep -q "$sbflag"; then
_fail "FAIL: feature $sbflag must not be set on the base image"
fi
- run_check $TOP/btrfstune $tuneopt $TEST_DEV
- if ! run_check_stdout $TOP/btrfs inspect-internal dump-super $TEST_DEV | \
+ run_check "$TOP/btrfstune" "$tuneopt" "$TEST_DEV"
+ if ! run_check_stdout "$TOP/btrfs" inspect-internal dump-super "$TEST_DEV" | \
grep -q "$sbflag"; then
_fail "FAIL: feature $sbflag not set"
fi
- run_check $SUDO_HELPER $TOP/btrfs check $TEST_DEV
+ run_check $SUDO_HELPER "$TOP/btrfs" check "$TEST_DEV"
}
test_feature extref -r EXTENDED_IREF
diff --git a/tests/misc-tests/002-uuid-rewrite/test.sh b/tests/misc-tests/002-uuid-rewrite/test.sh
index e32aff0c..0191bd5c 100755
--- a/tests/misc-tests/002-uuid-rewrite/test.sh
+++ b/tests/misc-tests/002-uuid-rewrite/test.sh
@@ -10,10 +10,7 @@ check_prereq btrfs
prepare_test_dev
get_fs_uuid() {
- local image
-
- image="$1"
- run_check_stdout $TOP/btrfs inspect-internal dump-super "$image" | \
+ run_check_stdout "$TOP/btrfs" inspect-internal dump-super "$1" | \
grep '^fsid' | awk '{print $2}'
}
@@ -23,18 +20,18 @@ test_uuid_random()
origuuid=11111111-a101-4031-b29a-379d4f8b7a2d
- run_check $SUDO_HELPER $TOP/mkfs.btrfs -f \
- --uuid $origuuid \
- --rootdir $INTERNAL_BIN/Documentation \
- $TEST_DEV
- run_check $TOP/btrfs inspect-internal dump-super "$TEST_DEV"
- currentfsid=$(run_check_stdout $TOP/btrfstune -f -u $TEST_DEV | \
+ run_check $SUDO_HELPER "$TOP/mkfs.btrfs" -f \
+ --uuid "$origuuid" \
+ --rootdir "$INTERNAL_BIN/Documentation" \
+ "$TEST_DEV"
+ run_check "$TOP/btrfs" inspect-internal dump-super "$TEST_DEV"
+ currentfsid=$(run_check_stdout "$TOP/btrfstune" -f -u "$TEST_DEV" | \
grep -i 'current fsid:' | awk '{print $3}')
- if ! [ $currentfsid = $origuuid ]; then
+ if ! [ "$currentfsid" = "$origuuid" ]; then
_fail "FAIL: current UUID mismatch"
fi
- run_check $TOP/btrfs inspect-internal dump-super "$TEST_DEV"
- run_check $SUDO_HELPER $TOP/btrfs check $TEST_DEV
+ run_check "$TOP/btrfs" inspect-internal dump-super "$TEST_DEV"
+ run_check $SUDO_HELPER "$TOP/btrfs" check "$TEST_DEV"
}
test_uuid_user()
@@ -45,19 +42,19 @@ test_uuid_user()
origuuid=22222222-d324-4f92-80e9-7658bf3b845f
newuuid=33333333-bfc9-4045-9399-a396dc6893b3
- run_check $SUDO_HELPER $TOP/mkfs.btrfs -f \
- --uuid $origuuid \
- --rootdir $INTERNAL_BIN/Documentation \
- $TEST_DEV
- run_check $TOP/btrfs inspect-internal dump-super "$TEST_DEV"
- run_check $TOP/btrfstune -f -U $newuuid \
- $TEST_DEV
+ run_check $SUDO_HELPER "$TOP/mkfs.btrfs" -f \
+ --uuid "$origuuid" \
+ --rootdir "$INTERNAL_BIN/Documentation" \
+ "$TEST_DEV"
+ run_check "$TOP/btrfs" inspect-internal dump-super "$TEST_DEV"
+ run_check "$TOP/btrfstune" -f -U "$newuuid" \
+ "$TEST_DEV"
# btrfs inspect-internal dump-super is called within get_fs_uuid
- fsid=$(get_fs_uuid $TEST_DEV)
- if ! [ $fsid = $newuuid ]; then
+ fsid=$(get_fs_uuid "$TEST_DEV")
+ if ! [ "$fsid" = "$newuuid" ]; then
_fail "FAIL: UUID not rewritten"
fi
- run_check $SUDO_HELPER $TOP/btrfs check $TEST_DEV
+ run_check $SUDO_HELPER "$TOP/btrfs" check "$TEST_DEV"
}
test_uuid_random
diff --git a/tests/misc-tests/003-zero-log/test.sh b/tests/misc-tests/003-zero-log/test.sh
index 9d2940f5..c6742bf3 100755
--- a/tests/misc-tests/003-zero-log/test.sh
+++ b/tests/misc-tests/003-zero-log/test.sh
@@ -9,42 +9,32 @@ prepare_test_dev
get_log_root()
{
- local image
-
- image="$1"
- $TOP/btrfs inspect-internal dump-super "$image" | \
+ "$TOP/btrfs" inspect-internal dump-super "$1" | \
grep '^log_root\>' | awk '{print $2}'
}
get_log_root_level() {
- local image
-
- image="$1"
- $TOP/btrfs inspect-internal dump-super "$image" | \
+ "$TOP/btrfs" inspect-internal dump-super "$1" | \
grep '^log_root_level' | awk '{print $2}'
}
test_zero_log()
{
# FIXME: we need an image with existing log_root
- run_check $SUDO_HELPER $TOP/mkfs.btrfs -f \
- --rootdir $INTERNAL_BIN/Documentation \
- $TEST_DEV
- run_check $TOP/btrfs inspect-internal dump-super $TEST_DEV
- if [ "$1" = 'standalone' ]; then
- run_check $TOP/btrfs rescue zero-log $TEST_DEV
- else
- run_check $TOP/btrfs-zero-log $TEST_DEV
- fi
- log_root=$(get_log_root $TEST_DEV)
- log_root_level=$(get_log_root $TEST_DEV)
+ run_check $SUDO_HELPER "$TOP/mkfs.btrfs" -f \
+ --rootdir "$INTERNAL_BIN/Documentation" \
+ "$TEST_DEV"
+ run_check "$TOP/btrfs" inspect-internal dump-super "$TEST_DEV"
+ run_check "$TOP/btrfs" rescue zero-log "$TEST_DEV"
+ log_root=$(get_log_root "$TEST_DEV")
+ log_root_level=$(get_log_root "$TEST_DEV")
if [ "$log_root" != 0 ]; then
_fail "FAIL: log_root not reset"
fi
if [ "$log_root_level" != 0 ]; then
_fail "FAIL: log_root_level not reset"
fi
- run_check $TOP/btrfs inspect-internal dump-super $TEST_DEV
- run_check $SUDO_HELPER $TOP/btrfs check $TEST_DEV
+ run_check "$TOP/btrfs" inspect-internal dump-super "$TEST_DEV"
+ run_check $SUDO_HELPER "$TOP/btrfs" check "$TEST_DEV"
}
test_zero_log standalone
diff --git a/tests/misc-tests/004-shrink-fs/test.sh b/tests/misc-tests/004-shrink-fs/test.sh
index 2f08b0b0..4117f037 100755
--- a/tests/misc-tests/004-shrink-fs/test.sh
+++ b/tests/misc-tests/004-shrink-fs/test.sh
@@ -14,45 +14,45 @@ setup_root_helper
# Optionally take id of the device to shrink
shrink_test()
{
- min_size=$(run_check_stdout $SUDO_HELPER $TOP/btrfs inspect-internal min-dev-size ${1:+--id $1} $TEST_MNT)
- min_size=$(echo $min_size | cut -d ' ' -f 1)
- echo "min size = ${min_size}" >> $RESULTS
+ min_size=$(run_check_stdout $SUDO_HELPER "$TOP/btrfs" inspect-internal min-dev-size ${1:+--id "$1"} "$TEST_MNT")
+ min_size=$(echo "$min_size" | cut -d ' ' -f 1)
+ echo "min size = ${min_size}" >> "$RESULTS"
if [ -z "$min_size" ]; then
_fail "Failed to parse minimum size"
fi
- run_check $SUDO_HELPER $TOP/btrfs filesystem resize $min_size $TEST_MNT
+ run_check $SUDO_HELPER "$TOP/btrfs" filesystem resize "$min_size" "$TEST_MNT"
}
-run_check truncate -s 20G $IMAGE
-run_check $TOP/mkfs.btrfs -f $IMAGE
-run_check $SUDO_HELPER mount $IMAGE $TEST_MNT
-run_check $SUDO_HELPER chmod a+rw $TEST_MNT
+run_check truncate -s 20G "$IMAGE"
+run_check "$TOP/mkfs.btrfs" -f "$IMAGE"
+run_check $SUDO_HELPER mount "$IMAGE" "$TEST_MNT"
+run_check $SUDO_HELPER chmod a+rw "$TEST_MNT"
# Create 7 data block groups, each with a size of 1Gb.
for ((i = 1; i <= 7; i++)); do
- run_check fallocate -l 1G $TEST_MNT/foo$i
+ run_check fallocate -l 1G "$TEST_MNT/foo$i"
done
# Make sure they are persisted (all the chunk, device and block group items
# added to the chunk/dev/extent trees).
-run_check $TOP/btrfs filesystem sync $TEST_MNT
+run_check "$TOP/btrfs" filesystem sync "$TEST_MNT"
# Now remove 3 of those 1G files. This will result in 3 block groups becoming
# unused, which will be automatically deleted by the cleaner kthread, and this
# will result in 3 holes (unallocated space) in the device (each with a size
# of 1Gb).
-run_check rm -f $TEST_MNT/foo2
-run_check rm -f $TEST_MNT/foo4
-run_check rm -f $TEST_MNT/foo6
+run_check rm -f "$TEST_MNT/foo2"
+run_check rm -f "$TEST_MNT/foo4"
+run_check rm -f "$TEST_MNT/foo6"
# Sync once to wake up the cleaner kthread which will delete the unused block
# groups - it could have been sleeping when they became unused. Then wait a bit
# to allow the cleaner kthread to delete them and then finally ensure the
# transaction started by the cleaner kthread is committed.
-run_check $TOP/btrfs filesystem sync $TEST_MNT
+run_check "$TOP/btrfs" filesystem sync "$TEST_MNT"
sleep 3
-run_check $TOP/btrfs filesystem sync $TEST_MNT
+run_check "$TOP/btrfs" filesystem sync "$TEST_MNT"
# Now attempt to get the minimum size we can resize the filesystem to and verify
# the resize operation succeeds. This size closely matches the sum of the size
@@ -63,10 +63,10 @@ done
# Now convert metadata and system chunks to the single profile and check we are
# still able to get a correct minimum size and shrink to that size.
-run_check $SUDO_HELPER $TOP/btrfs balance start -mconvert=single \
- -sconvert=single -f $TEST_MNT
+run_check $SUDO_HELPER "$TOP/btrfs" balance start -mconvert=single \
+ -sconvert=single -f "$TEST_MNT"
for ((i = 1; i <= 3; i++)); do
shrink_test 1
done
-run_check $SUDO_HELPER umount $TEST_MNT
+run_check $SUDO_HELPER umount "$TEST_MNT"
diff --git a/tests/misc-tests/005-convert-progress-thread-crash/test.sh b/tests/misc-tests/005-convert-progress-thread-crash/test.sh
index b8012c9f..dcd6fd26 100755
--- a/tests/misc-tests/005-convert-progress-thread-crash/test.sh
+++ b/tests/misc-tests/005-convert-progress-thread-crash/test.sh
@@ -9,7 +9,7 @@ mkfs.ext4 -V &>/dev/null || _not_run "mkfs.ext4 not found"
prepare_test_dev
for ((i = 0; i < 20; i++)); do
- echo "loop $i" >>$RESULTS
- mkfs.ext4 -F "$TEST_DEV" &>>$RESULTS || _not_run "mkfs.ext4 failed"
- run_check $TOP/btrfs-convert "$TEST_DEV"
+ echo "loop $i" >> "$RESULTS"
+ mkfs.ext4 -F "$TEST_DEV" &>>"$RESULTS" || _not_run "mkfs.ext4 failed"
+ run_check "$TOP/btrfs-convert" "$TEST_DEV"
done
diff --git a/tests/misc-tests/006-image-on-missing-device/test.sh b/tests/misc-tests/006-image-on-missing-device/test.sh
index d8b1cef2..2b222340 100755
--- a/tests/misc-tests/006-image-on-missing-device/test.sh
+++ b/tests/misc-tests/006-image-on-missing-device/test.sh
@@ -14,29 +14,29 @@ setup_root_helper
test_image_dump()
{
- run_check $SUDO_HELPER $TOP/btrfs check $dev1
+ run_check $SUDO_HELPER "$TOP/btrfs" check "$dev1"
# the output file will be deleted
- run_mayfail $SUDO_HELPER $TOP/btrfs-image $dev1 /tmp/test-img.dump
+ run_mayfail $SUDO_HELPER "$TOP/btrfs-image" "$dev1" /tmp/test-img.dump
}
test_run()
{
- run_check $SUDO_HELPER $TOP/mkfs.btrfs -f -d raid1 -m raid1 $dev1 $dev2
+ run_check $SUDO_HELPER "$TOP/mkfs.btrfs" -f -d raid1 -m raid1 "$dev1" "$dev2"
# we need extents to trigger reading from all devices
- run_check $SUDO_HELPER mount $dev1 $TEST_MNT
- run_check $SUDO_HELPER dd if=/dev/zero of=$TEST_MNT/a bs=1M count=10
- run_check $SUDO_HELPER dd if=/dev/zero of=$TEST_MNT/b bs=4k count=1000 conv=sync
- run_check $SUDO_HELPER umount $TEST_MNT
+ run_check $SUDO_HELPER mount "$dev1" "$TEST_MNT"
+ run_check $SUDO_HELPER dd if=/dev/zero of="$TEST_MNT/a" bs=1M count=10
+ run_check $SUDO_HELPER dd if=/dev/zero of="$TEST_MNT/b" bs=4k count=1000 conv=sync
+ run_check $SUDO_HELPER umount "$TEST_MNT"
test_image_dump
- run_check $SUDO_HELPER $TOP/btrfs filesystem show $dev1
+ run_check $SUDO_HELPER "$TOP/btrfs" filesystem show "$dev1"
# create a degraded raid1 filesystem, check must succeed
# btrfs-image must not loop
- run_mayfail wipefs -a $dev2
- run_check $SUDO_HELPER losetup -d $dev2
+ run_mayfail wipefs -a "$dev2"
+ run_check $SUDO_HELPER losetup -d "$dev2"
unset loopdevs[2]
- run_check $SUDO_HELPER $TOP/btrfs filesystem show $dev1
+ run_check $SUDO_HELPER "$TOP/btrfs" filesystem show "$dev1"
test_image_dump
}
diff --git a/tests/misc-tests/007-subvolume-sync/test.sh b/tests/misc-tests/007-subvolume-sync/test.sh
index ef03d16b..104b50ea 100755
--- a/tests/misc-tests/007-subvolume-sync/test.sh
+++ b/tests/misc-tests/007-subvolume-sync/test.sh
@@ -12,21 +12,21 @@ check_prereq btrfs
setup_root_helper
prepare_test_dev
-run_check $SUDO_HELPER $TOP/mkfs.btrfs -f "$TEST_DEV"
+run_check $SUDO_HELPER "$TOP/mkfs.btrfs" -f "$TEST_DEV"
run_check_mount_test_dev
# to check following thing in both 1 and multiple subvolume case:
# 1: is subvolume sync loop indefinitely
# 2: is return value right
#
-run_check $SUDO_HELPER $TOP/btrfs subvolume create "$TEST_MNT"/mysubvol1
-run_check $SUDO_HELPER $TOP/btrfs subvolume create "$TEST_MNT"/mysubvol2
-run_check $SUDO_HELPER $TOP/btrfs subvolume delete "$TEST_MNT"/mysubvol1
-run_check $SUDO_HELPER $TOP/btrfs subvolume delete "$TEST_MNT"/mysubvol2
-run_check $SUDO_HELPER $TOP/btrfs subvolume sync "$TEST_MNT"
+run_check $SUDO_HELPER "$TOP/btrfs" subvolume create "$TEST_MNT"/mysubvol1
+run_check $SUDO_HELPER "$TOP/btrfs" subvolume create "$TEST_MNT"/mysubvol2
+run_check $SUDO_HELPER "$TOP/btrfs" subvolume delete "$TEST_MNT"/mysubvol1
+run_check $SUDO_HELPER "$TOP/btrfs" subvolume delete "$TEST_MNT"/mysubvol2
+run_check $SUDO_HELPER "$TOP/btrfs" subvolume sync "$TEST_MNT"
-run_check $SUDO_HELPER $TOP/btrfs subvolume create "$TEST_MNT"/mysubvol
-run_check $SUDO_HELPER $TOP/btrfs subvolume delete "$TEST_MNT"/mysubvol
-run_check $SUDO_HELPER $TOP/btrfs subvolume sync "$TEST_MNT"
+run_check $SUDO_HELPER "$TOP/btrfs" subvolume create "$TEST_MNT"/mysubvol
+run_check $SUDO_HELPER "$TOP/btrfs" subvolume delete "$TEST_MNT"/mysubvol
+run_check $SUDO_HELPER "$TOP/btrfs" subvolume sync "$TEST_MNT"
run_check_umount_test_dev
diff --git a/tests/misc-tests/008-leaf-crossing-stripes/test.sh b/tests/misc-tests/008-leaf-crossing-stripes/test.sh
index 517bd667..41825439 100755
--- a/tests/misc-tests/008-leaf-crossing-stripes/test.sh
+++ b/tests/misc-tests/008-leaf-crossing-stripes/test.sh
@@ -15,8 +15,8 @@ A_PRIME_NUM=17
for ((size = SIZE_FROM; size <= SIZE_END; size += A_PRIME_NUM)); do
run_check truncate -s "$size"M "$IMAGE"
run_check mkfs.ext4 -F "$IMAGE"
- run_check $TOP/btrfs-convert "$IMAGE"
- run_check_stdout $TOP/btrfs check "$IMAGE" 2>&1 |
+ run_check "$TOP/btrfs-convert" "$IMAGE"
+ run_check_stdout "$TOP/btrfs" check "$IMAGE" 2>&1 |
grep -q "crossing stripe boundary" &&
_fail "leaf crossing stripes after btrfs-convert"
done
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 15de3355..62190e14 100755
--- a/tests/misc-tests/009-subvolume-sync-must-wait/test.sh
+++ b/tests/misc-tests/009-subvolume-sync-must-wait/test.sh
@@ -10,41 +10,41 @@ check_prereq btrfs
setup_root_helper
prepare_test_dev
-run_check $TOP/mkfs.btrfs -f "$TEST_DEV"
+run_check "$TOP/mkfs.btrfs" -f "$TEST_DEV"
run_check_mount_test_dev
-run_check $SUDO_HELPER chmod a+rw $TEST_MNT
+run_check $SUDO_HELPER chmod a+rw "$TEST_MNT"
-cd $TEST_MNT
+cd "$TEST_MNT"
for i in `seq 5`; do
run_check dd if=/dev/zero of=file$i bs=1M count=10
done
for sn in `seq 4`;do
- run_check $SUDO_HELPER $TOP/btrfs subvolume snapshot . snap$sn
+ run_check $SUDO_HELPER "$TOP/btrfs" subvolume snapshot . snap$sn
for i in `seq 10`; do
- run_check dd if=/dev/zero of=snap$sn/file$i bs=1M count=10
+ run_check dd if=/dev/zero of="snap$sn/file$i" bs=1M count=10
done
done
-run_check $SUDO_HELPER $TOP/btrfs subvolume list .
-run_check $SUDO_HELPER $TOP/btrfs subvolume list -d .
+run_check $SUDO_HELPER "$TOP/btrfs" subvolume list .
+run_check $SUDO_HELPER "$TOP/btrfs" subvolume list -d .
-idtodel=`run_check_stdout $SUDO_HELPER $TOP/btrfs inspect-internal rootid snap3`
+idtodel=`run_check_stdout $SUDO_HELPER "$TOP/btrfs" inspect-internal rootid snap3`
# delete, sync after some time
-run_check $SUDO_HELPER $TOP/btrfs subvolume delete -c snap3
-{ sleep 5; run_check $TOP/btrfs filesystem sync $TEST_MNT; } &
+run_check $SUDO_HELPER "$TOP/btrfs" subvolume delete -c snap3
+{ sleep 5; run_check "$TOP/btrfs" filesystem sync "$TEST_MNT"; } &
-run_check $SUDO_HELPER $TOP/btrfs subvolume sync . $idtodel
+run_check $SUDO_HELPER "$TOP/btrfs" subvolume sync . "$idtodel"
-if run_check_stdout $SUDO_HELPER $TOP/btrfs subvolume list -d . |
+if run_check_stdout $SUDO_HELPER "$TOP/btrfs" subvolume list -d . |
grep -q "ID $idtodel.*DELETED"; then
_fail "sync did not wait for the subvolume cleanup"
fi
-run_check $TOP/btrfs filesystem sync $TEST_MNT
-run_check $SUDO_HELPER $TOP/btrfs subvolume list -d .
+run_check "$TOP/btrfs" filesystem sync "$TEST_MNT"
+run_check $SUDO_HELPER "$TOP/btrfs" subvolume list -d .
wait
cd ..
diff --git a/tests/misc-tests/010-convert-delete-ext2-subvol/test.sh b/tests/misc-tests/010-convert-delete-ext2-subvol/test.sh
index 6d510fea..5f441a7f 100755
--- a/tests/misc-tests/010-convert-delete-ext2-subvol/test.sh
+++ b/tests/misc-tests/010-convert-delete-ext2-subvol/test.sh
@@ -13,13 +13,13 @@ prepare_test_dev
run_check truncate -s 2G "$TEST_DEV"
run_check mkfs.ext4 -F "$TEST_DEV"
-run_check $TOP/btrfs-convert "$TEST_DEV"
-run_check $SUDO_HELPER $TOP/btrfs inspect-internal dump-tree "$TEST_DEV"
+run_check "$TOP/btrfs-convert" "$TEST_DEV"
+run_check $SUDO_HELPER "$TOP/btrfs" inspect-internal dump-tree "$TEST_DEV"
run_check_mount_test_dev
-run_check $SUDO_HELPER $TOP/btrfs subvolume delete -c "$TEST_MNT/ext2_saved"
+run_check $SUDO_HELPER "$TOP/btrfs" subvolume delete -c "$TEST_MNT/ext2_saved"
run_check_umount_test_dev
-run_check $SUDO_HELPER $TOP/btrfs inspect-internal dump-tree "$TEST_DEV"
-run_check_stdout $TOP/btrfs-convert --rollback "$TEST_DEV" |
+run_check $SUDO_HELPER "$TOP/btrfs" inspect-internal dump-tree "$TEST_DEV"
+run_check_stdout "$TOP/btrfs-convert" --rollback "$TEST_DEV" |
grep -q 'is it deleted' || _fail "unexpected rollback"
exit 0
diff --git a/tests/misc-tests/011-delete-missing-device/test.sh b/tests/misc-tests/011-delete-missing-device/test.sh
index 469c3be9..4c976421 100755
--- a/tests/misc-tests/011-delete-missing-device/test.sh
+++ b/tests/misc-tests/011-delete-missing-device/test.sh
@@ -10,31 +10,31 @@ setup_root_helper
test_do_mkfs()
{
- run_check $SUDO_HELPER $TOP/mkfs.btrfs -f $@ ${loopdevs[@]}
- run_check $SUDO_HELPER $TOP/btrfs inspect-internal dump-super $dev1
- run_check $SUDO_HELPER $TOP/btrfs check $dev1
- run_check $SUDO_HELPER $TOP/btrfs filesystem show
+ run_check $SUDO_HELPER "$TOP/mkfs.btrfs" -f "$@" ${loopdevs[@]}
+ run_check $SUDO_HELPER "$TOP/btrfs" inspect-internal dump-super "$dev1"
+ run_check $SUDO_HELPER "$TOP/btrfs" check "$dev1"
+ run_check $SUDO_HELPER "$TOP/btrfs" filesystem show
}
test_wipefs()
{
- run_check $SUDO_HELPER wipefs -a $devtodel
- run_check $SUDO_HELPER losetup -d $devtodel
+ run_check $SUDO_HELPER wipefs -a "$devtodel"
+ run_check $SUDO_HELPER losetup -d "$devtodel"
unset loopdevs[3]
run_check $SUDO_HELPER losetup --all
- run_check $TOP/btrfs filesystem show
+ run_check "$TOP/btrfs" filesystem show
}
test_delete_missing()
{
run_check_mount_test_dev -o degraded
- run_check $SUDO_HELPER $TOP/btrfs filesystem show $TEST_MNT
- run_check $SUDO_HELPER $TOP/btrfs device delete missing $TEST_MNT
- run_check $SUDO_HELPER $TOP/btrfs filesystem show $TEST_MNT
+ run_check $SUDO_HELPER "$TOP/btrfs" filesystem show "$TEST_MNT"
+ run_check $SUDO_HELPER "$TOP/btrfs" device delete missing "$TEST_MNT"
+ run_check $SUDO_HELPER "$TOP/btrfs" filesystem show "$TEST_MNT"
run_check_umount_test_dev
run_check_mount_test_dev
local out
- out="$(run_check_stdout $SUDO_HELPER $TOP/btrfs filesystem show $TEST_MNT)"
+ out=$(run_check_stdout $SUDO_HELPER "$TOP/btrfs" filesystem show "$TEST_MNT")
if echo "$out" | grep -q -- "$devtodel"; then
_fail "device $devtodel not deleted"
fi
diff --git a/tests/misc-tests/012-find-root-no-result/test.sh b/tests/misc-tests/012-find-root-no-result/test.sh
index f4a57e76..6dd447f3 100755
--- a/tests/misc-tests/012-find-root-no-result/test.sh
+++ b/tests/misc-tests/012-find-root-no-result/test.sh
@@ -8,10 +8,10 @@ source "$TEST_TOP/common"
check_prereq btrfs-find-root
check_prereq btrfs-image
-run_check $TOP/btrfs-image -r first_meta_chunk.btrfs-image test.img || \
+run_check "$TOP/btrfs-image" -r first_meta_chunk.btrfs-image test.img || \
_fail "failed to extract first_meta_chunk.btrfs-image"
-result=$(run_check_stdout $TOP/btrfs-find-root test.img | sed '/^Superblock/d')
+result=$(run_check_stdout "$TOP/btrfs-find-root" test.img | sed '/^Superblock/d')
if [ -z "$result" ]; then
_fail "btrfs-find-root failed to find tree root"
diff --git a/tests/misc-tests/013-subvolume-sync-crash/test.sh b/tests/misc-tests/013-subvolume-sync-crash/test.sh
index 051b457a..c348ba5d 100755
--- a/tests/misc-tests/013-subvolume-sync-crash/test.sh
+++ b/tests/misc-tests/013-subvolume-sync-crash/test.sh
@@ -13,9 +13,9 @@ setup_root_helper
prepare_test_dev
run_check "$TOP/mkfs.btrfs" -f "$TEST_DEV"
run_check_mount_test_dev
-run_check $SUDO_HELPER chmod a+rw $TEST_MNT
+run_check $SUDO_HELPER chmod a+rw "$TEST_MNT"
-cd $TEST_MNT
+cd "$TEST_MNT"
for i in `seq 5`; do
run_check dd if=/dev/zero of=file$i bs=1M count=10
@@ -23,25 +23,25 @@ done
# 128 is minimum
for sn in `seq 130`;do
- run_check $SUDO_HELPER $TOP/btrfs subvolume snapshot . snap$sn
+ run_check $SUDO_HELPER "$TOP/btrfs" subvolume snapshot . snap$sn
for i in `seq 10`; do
- run_check dd if=/dev/zero of=snap$sn/file$i bs=1M count=1
+ run_check dd if=/dev/zero of="snap$sn/file$i" bs=1M count=1
done
done
-run_check $SUDO_HELPER $TOP/btrfs subvolume list .
-run_check $SUDO_HELPER $TOP/btrfs subvolume list -d .
+run_check $SUDO_HELPER "$TOP/btrfs" subvolume list .
+run_check $SUDO_HELPER "$TOP/btrfs" subvolume list -d .
-idtodel=`run_check_stdout $SUDO_HELPER $TOP/btrfs inspect-internal rootid snap3`
+idtodel=`run_check_stdout $SUDO_HELPER "$TOP/btrfs" inspect-internal rootid snap3`
# delete, sync after some time
-run_check $SUDO_HELPER $TOP/btrfs subvolume delete -c snap*
-{ sleep 5; run_check $TOP/btrfs filesystem sync $TEST_MNT; } &
+run_check $SUDO_HELPER "$TOP/btrfs" subvolume delete -c snap*
+{ sleep 5; run_check "$TOP/btrfs" filesystem sync "$TEST_MNT"; } &
-run_check $SUDO_HELPER $TOP/btrfs subvolume sync .
+run_check $SUDO_HELPER "$TOP/btrfs" subvolume sync .
-run_check $TOP/btrfs filesystem sync $TEST_MNT
-run_check $SUDO_HELPER $TOP/btrfs subvolume list -d .
+run_check "$TOP/btrfs" filesystem sync "$TEST_MNT"
+run_check $SUDO_HELPER "$TOP/btrfs" subvolume list -d .
wait
cd ..
diff --git a/tests/misc-tests/014-filesystem-label/test.sh b/tests/misc-tests/014-filesystem-label/test.sh
index bd6773cb..4756e764 100755
--- a/tests/misc-tests/014-filesystem-label/test.sh
+++ b/tests/misc-tests/014-filesystem-label/test.sh
@@ -12,14 +12,14 @@ setup_root_helper
prepare_test_dev
run_check "$TOP/mkfs.btrfs" -L BTRFS-TEST-LABEL -f "$TEST_DEV"
run_check_mount_test_dev
-run_check $SUDO_HELPER chmod a+rw $TEST_MNT
+run_check $SUDO_HELPER chmod a+rw "$TEST_MNT"
-cd $TEST_MNT
-run_check $SUDO_HELPER $TOP/btrfs filesystem label $TEST_MNT
+cd "$TEST_MNT"
+run_check $SUDO_HELPER "$TOP/btrfs" filesystem label "$TEST_MNT"
# shortest label
-run_check $SUDO_HELPER $TOP/btrfs filesystem label $TEST_MNT a
-run_check $SUDO_HELPER $TOP/btrfs filesystem label $TEST_MNT
-run_check $SUDO_HELPER $TOP/btrfs filesystem label $TEST_MNT ''
+run_check $SUDO_HELPER "$TOP/btrfs" filesystem label "$TEST_MNT" a
+run_check $SUDO_HELPER "$TOP/btrfs" filesystem label "$TEST_MNT"
+run_check $SUDO_HELPER "$TOP/btrfs" filesystem label "$TEST_MNT" ''
longlabel=\
0123456789\
@@ -54,15 +54,15 @@ longlabel=\
\
01234
-run_check $SUDO_HELPER $TOP/btrfs filesystem label $TEST_MNT "$longlabel"
-run_check $SUDO_HELPER $TOP/btrfs filesystem label $TEST_MNT
+run_check $SUDO_HELPER "$TOP/btrfs" filesystem label "$TEST_MNT" "$longlabel"
+run_check $SUDO_HELPER "$TOP/btrfs" filesystem label "$TEST_MNT"
# 256, must fail
run_mustfail "label 256 bytes long succeeded" \
- $SUDO_HELPER $TOP/btrfs filesystem label $TEST_MNT "$longlabel"5
-run_check $SUDO_HELPER $TOP/btrfs filesystem label $TEST_MNT
+ $SUDO_HELPER "$TOP/btrfs" filesystem label "$TEST_MNT" "$longlabel"5
+run_check $SUDO_HELPER "$TOP/btrfs" filesystem label "$TEST_MNT"
run_mustfail "label 2 * 255 bytes long succeeded" \
- $SUDO_HELPER $TOP/btrfs filesystem label $TEST_MNT "$longlabel$longlabel"
-run_check $SUDO_HELPER $TOP/btrfs filesystem label $TEST_MNT
+ $SUDO_HELPER "$TOP/btrfs" filesystem label "$TEST_MNT" "$longlabel$longlabel"
+run_check $SUDO_HELPER "$TOP/btrfs" filesystem label "$TEST_MNT"
cd ..
diff --git a/tests/misc-tests/015-dump-super-garbage/test.sh b/tests/misc-tests/015-dump-super-garbage/test.sh
index 10d8d5b6..b3469459 100755
--- a/tests/misc-tests/015-dump-super-garbage/test.sh
+++ b/tests/misc-tests/015-dump-super-garbage/test.sh
@@ -6,12 +6,12 @@ source "$TEST_TOP/common"
check_prereq btrfs
-run_check $TOP/btrfs inspect-internal dump-super /dev/urandom
-run_check $TOP/btrfs inspect-internal dump-super -a /dev/urandom
-run_check $TOP/btrfs inspect-internal dump-super -fa /dev/urandom
-run_check $TOP/btrfs inspect-internal dump-super -Ffa /dev/urandom
-run_check $TOP/btrfs inspect-internal dump-super -Ffa /dev/urandom
-run_check $TOP/btrfs inspect-internal dump-super -Ffa /dev/urandom
-run_check $TOP/btrfs inspect-internal dump-super -Ffa /dev/urandom
-run_check $TOP/btrfs inspect-internal dump-super -Ffa /dev/urandom
-run_check $TOP/btrfs inspect-internal dump-super -Ffa /dev/urandom
+run_check "$TOP/btrfs" inspect-internal dump-super /dev/urandom
+run_check "$TOP/btrfs" inspect-internal dump-super -a /dev/urandom
+run_check "$TOP/btrfs" inspect-internal dump-super -fa /dev/urandom
+run_check "$TOP/btrfs" inspect-internal dump-super -Ffa /dev/urandom
+run_check "$TOP/btrfs" inspect-internal dump-super -Ffa /dev/urandom
+run_check "$TOP/btrfs" inspect-internal dump-super -Ffa /dev/urandom
+run_check "$TOP/btrfs" inspect-internal dump-super -Ffa /dev/urandom
+run_check "$TOP/btrfs" inspect-internal dump-super -Ffa /dev/urandom
+run_check "$TOP/btrfs" inspect-internal dump-super -Ffa /dev/urandom
diff --git a/tests/misc-tests/016-send-clone-src/test.sh b/tests/misc-tests/016-send-clone-src/test.sh
index e4fa16a7..c993095e 100755
--- a/tests/misc-tests/016-send-clone-src/test.sh
+++ b/tests/misc-tests/016-send-clone-src/test.sh
@@ -17,28 +17,28 @@ run_check_mount_test_dev
here=`pwd`
cd "$TEST_MNT" || _fail "cannot chdir to TEST_MNT"
-run_check $SUDO_HELPER $TOP/btrfs subvolume create subv-parent1
+run_check $SUDO_HELPER "$TOP/btrfs" subvolume create subv-parent1
for i in 1 2 3; do
run_check $SUDO_HELPER dd if=/dev/zero of=subv-parent1/file1_$i bs=1M count=1
- run_check $SUDO_HELPER $TOP/btrfs subvolume snapshot -r subv-parent1 subv-snap1_$i
+ run_check $SUDO_HELPER "$TOP/btrfs" subvolume snapshot -r subv-parent1 subv-snap1_$i
done
-run_check $SUDO_HELPER $TOP/btrfs subvolume create subv-parent2
+run_check $SUDO_HELPER "$TOP/btrfs" subvolume create subv-parent2
for i in 1 2 3; do
run_check $SUDO_HELPER dd if=/dev/zero of=subv-parent2/file2_$i bs=1M count=1
- run_check $SUDO_HELPER $TOP/btrfs subvolume snapshot -r subv-parent2 subv-snap2_$i
+ run_check $SUDO_HELPER "$TOP/btrfs" subvolume snapshot -r subv-parent2 subv-snap2_$i
done
truncate -s0 "$here"/send-stream.img
chmod a+w "$here"/send-stream.img
-run_check $SUDO_HELPER $TOP/btrfs send -f "$here"/send-stream.img \
+run_check $SUDO_HELPER "$TOP/btrfs" send -f "$here"/send-stream.img \
-c subv-snap1_1 -c subv-snap2_1 subv-snap1_[23] subv-snap2_[23]
image=$(extract_image "$here"/multi-clone-src-v4.8.2.stream.xz)
old_stream_size=`stat --format=%s "$image"`
stream_size=`stat --format=%s "$here"/send-stream.img`
-if [ $old_stream_size -lt $stream_size ]; then
+if [ "$old_stream_size" -lt "$stream_size" ]; then
run_check ls -l "$image" "$here"/send-stream.img
_fail "sending stream size is bigger than old stream"
fi
diff --git a/tests/misc-tests/018-recv-end-of-stream/test.sh b/tests/misc-tests/018-recv-end-of-stream/test.sh
index 79e735ea..0de7d3b6 100755
--- a/tests/misc-tests/018-recv-end-of-stream/test.sh
+++ b/tests/misc-tests/018-recv-end-of-stream/test.sh
@@ -19,24 +19,24 @@ test_full_empty_stream() {
local str
str="$here/stream-full-empty.stream"
- run_check $TOP/mkfs.btrfs -f $TEST_DEV
+ run_check "$TOP/mkfs.btrfs" -f "$TEST_DEV"
run_check_mount_test_dev
cd "$TEST_MNT" || _fail "cannot chdir to TEST_MNT"
- run_check $SUDO_HELPER $TOP/btrfs subvolume create subv1
- run_check $SUDO_HELPER $TOP/btrfs subvolume snapshot -r subv1 subv1-snap
+ run_check $SUDO_HELPER "$TOP/btrfs" subvolume create subv1
+ run_check $SUDO_HELPER "$TOP/btrfs" subvolume snapshot -r subv1 subv1-snap
truncate -s0 "$str"
chmod a+w "$str"
- run_check $SUDO_HELPER $TOP/btrfs send -f "$str" subv1-snap
+ run_check $SUDO_HELPER "$TOP/btrfs" send -f "$str" subv1-snap
cd "$here" || _fail "cannot chdir back to test directory"
run_check_umount_test_dev
- run_check $TOP/mkfs.btrfs -f $TEST_DEV
+ run_check "$TOP/mkfs.btrfs" -f "$TEST_DEV"
run_check_mount_test_dev
- run_check $SUDO_HELPER $TOP/btrfs receive "$@" -v -f "$str" "$TEST_MNT"
+ run_check $SUDO_HELPER "$TOP/btrfs" receive "$@" -v -f "$str" "$TEST_MNT"
run_check_umount_test_dev
run_check rm -f -- "$str"
@@ -46,28 +46,28 @@ test_full_simple_stream() {
local str
str="$here/stream-full-simple.stream"
- run_check $TOP/mkfs.btrfs -f $TEST_DEV
+ run_check "$TOP/mkfs.btrfs" -f $TEST_DEV
run_check_mount_test_dev
cd "$TEST_MNT" || _fail "cannot chdir to TEST_MNT"
- run_check $SUDO_HELPER $TOP/btrfs subvolume create subv1
+ run_check $SUDO_HELPER "$TOP/btrfs" subvolume create subv1
for i in 1 2 3; do
run_check $SUDO_HELPER dd if=/dev/zero of=subv1/file1_$i bs=1M count=1
done
- run_check $SUDO_HELPER $TOP/btrfs subvolume snapshot -r subv1 subv1-snap
+ run_check $SUDO_HELPER "$TOP/btrfs" subvolume snapshot -r subv1 subv1-snap
truncate -s0 "$str"
chmod a+w "$str"
- run_check $SUDO_HELPER $TOP/btrfs send -f "$str" subv1-snap
+ run_check $SUDO_HELPER "$TOP/btrfs" send -f "$str" subv1-snap
cd "$here" || _fail "cannot chdir back to test directory"
run_check_umount_test_dev
- run_check $TOP/mkfs.btrfs -f $TEST_DEV
+ run_check "$TOP/mkfs.btrfs" -f "$TEST_DEV"
run_check_mount_test_dev
- run_check $SUDO_HELPER $TOP/btrfs receive "$@" -v -f "$str" "$TEST_MNT"
+ run_check $SUDO_HELPER "$TOP/btrfs" receive "$@" -v -f "$str" "$TEST_MNT"
run_check_umount_test_dev
run_check rm -f -- "$str"
@@ -79,27 +79,27 @@ test_incr_empty_stream() {
fstr="$here/stream-full-empty.stream"
istr="$here/stream-incr-empty.stream"
- run_check $TOP/mkfs.btrfs -f $TEST_DEV
+ run_check "$TOP/mkfs.btrfs" -f "$TEST_DEV"
run_check_mount_test_dev
cd "$TEST_MNT" || _fail "cannot chdir to TEST_MNT"
- run_check $SUDO_HELPER $TOP/btrfs subvolume create subv1
- run_check $SUDO_HELPER $TOP/btrfs subvolume snapshot -r subv1 subv1-snap
- run_check $SUDO_HELPER $TOP/btrfs subvolume snapshot -r subv1 subv2-snap
+ run_check $SUDO_HELPER "$TOP/btrfs" subvolume create subv1
+ run_check $SUDO_HELPER "$TOP/btrfs" subvolume snapshot -r subv1 subv1-snap
+ run_check $SUDO_HELPER "$TOP/btrfs" subvolume snapshot -r subv1 subv2-snap
truncate -s0 "$fstr" "$istr"
chmod a+w "$fstr" "$istr"
- run_check $SUDO_HELPER $TOP/btrfs send -f "$fstr" subv1-snap
- run_check $SUDO_HELPER $TOP/btrfs send -p subv1-snap -f "$istr" subv2-snap
+ run_check $SUDO_HELPER "$TOP/btrfs" send -f "$fstr" subv1-snap
+ run_check $SUDO_HELPER "$TOP/btrfs" send -p subv1-snap -f "$istr" subv2-snap
cd "$here" || _fail "cannot chdir back to test directory"
run_check_umount_test_dev
- run_check $TOP/mkfs.btrfs -f $TEST_DEV
+ run_check "$TOP/mkfs.btrfs" -f "$TEST_DEV"
run_check_mount_test_dev
- run_check $SUDO_HELPER $TOP/btrfs receive "$@" -v -f "$fstr" "$TEST_MNT"
- run_check $SUDO_HELPER $TOP/btrfs receive "$@" -v -f "$istr" "$TEST_MNT"
+ run_check $SUDO_HELPER "$TOP/btrfs" receive "$@" -v -f "$fstr" "$TEST_MNT"
+ run_check $SUDO_HELPER "$TOP/btrfs" receive "$@" -v -f "$istr" "$TEST_MNT"
run_check_umount_test_dev
run_check rm -f -- "$fstr" "$istr"
@@ -110,36 +110,36 @@ test_incr_simple_stream() {
fstr="$here/stream-full-simple.stream"
istr="$here/stream-incr-simple.stream"
- run_check $TOP/mkfs.btrfs -f $TEST_DEV
+ run_check "$TOP/mkfs.btrfs" -f "$TEST_DEV"
run_check_mount_test_dev
cd "$TEST_MNT" || _fail "cannot chdir to TEST_MNT"
- run_check $SUDO_HELPER $TOP/btrfs subvolume create subv1
+ run_check $SUDO_HELPER "$TOP/btrfs" subvolume create subv1
for i in 1 2 3; do
run_check $SUDO_HELPER dd if=/dev/zero of=subv1/file1_$i bs=1M count=1
done
- run_check $SUDO_HELPER $TOP/btrfs subvolume snapshot -r subv1 subv1-snap
+ run_check $SUDO_HELPER "$TOP/btrfs" subvolume snapshot -r subv1 subv1-snap
for i in 1 2 3; do
run_check $SUDO_HELPER dd if=/dev/urandom of=subv1/file1_$i bs=1M count=1
done
- run_check $SUDO_HELPER $TOP/btrfs subvolume snapshot -r subv1 subv2-snap
+ run_check $SUDO_HELPER "$TOP/btrfs" subvolume snapshot -r subv1 subv2-snap
truncate -s0 "$fstr" "$istr"
chmod a+w "$fstr" "$istr"
- run_check $SUDO_HELPER $TOP/btrfs send -f "$fstr" subv1-snap
- run_check $SUDO_HELPER $TOP/btrfs send -p subv1-snap -f "$istr" subv2-snap
+ run_check $SUDO_HELPER "$TOP/btrfs" send -f "$fstr" subv1-snap
+ run_check $SUDO_HELPER "$TOP/btrfs" send -p subv1-snap -f "$istr" subv2-snap
cd "$here" || _fail "cannot chdir back to test directory"
run_check_umount_test_dev
- run_check $TOP/mkfs.btrfs -f $TEST_DEV
+ run_check "$TOP/mkfs.btrfs" -f "$TEST_DEV"
run_check_mount_test_dev
- run_check $SUDO_HELPER $TOP/btrfs receive "$@" -v -f "$fstr" "$TEST_MNT"
- run_check $SUDO_HELPER $TOP/btrfs receive "$@" -v -f "$istr" "$TEST_MNT"
+ run_check $SUDO_HELPER "$TOP/btrfs" receive "$@" -v -f "$fstr" "$TEST_MNT"
+ run_check $SUDO_HELPER "$TOP/btrfs" receive "$@" -v -f "$istr" "$TEST_MNT"
run_check_umount_test_dev
run_check rm -f -- "$fstr" "$istr"
@@ -151,7 +151,7 @@ test_incr_empty_stream
test_incr_simple_stream
extra_opt=-e
-test_full_empty_stream $extra_opt
-test_full_simple_stream $extra_opt
-test_incr_empty_stream $extra_opt
-test_incr_simple_stream $extra_opt
+test_full_empty_stream "$extra_opt"
+test_full_simple_stream "$extra_opt"
+test_incr_empty_stream "$extra_opt"
+test_incr_simple_stream "$extra_opt"
diff --git a/tests/misc-tests/019-receive-clones-on-mounted-subvol/test.sh b/tests/misc-tests/019-receive-clones-on-mounted-subvol/test.sh
index 60ec5cf9..6937163a 100755
--- a/tests/misc-tests/019-receive-clones-on-mounted-subvol/test.sh
+++ b/tests/misc-tests/019-receive-clones-on-mounted-subvol/test.sh
@@ -31,9 +31,9 @@ run_check $SUDO_HELPER "$TOP/btrfs" subvolume create "$TEST_MNT/baz"
run_check $SUDO_HELPER "$TOP/btrfs" subvolume create "$TEST_MNT/snap"
tr '\000' 'A' < /dev/null |
- run_check $SUDO_HELPER dd of=$TEST_MNT/foo/file_a bs=$BLOCK_SIZE count=32
+ run_check $SUDO_HELPER dd of="$TEST_MNT/foo/file_a" bs="$BLOCK_SIZE" count=32
tr '\000' 'B' < /dev/null |
- run_check $SUDO_HELPER dd of=$TEST_MNT/bar/file_b bs=$BLOCK_SIZE count=32
+ run_check $SUDO_HELPER dd of="$TEST_MNT/bar/file_b" bs="$BLOCK_SIZE" count=32
run_check $SUDO_HELPER cp --reflink=always "$TEST_MNT/foo/file_a" "$TEST_MNT/baz/file_a"
run_check $SUDO_HELPER cp --reflink=always "$TEST_MNT/bar/file_b" "$TEST_MNT/baz/file_b"
diff --git a/tests/misc-tests/021-image-multi-devices/test.sh b/tests/misc-tests/021-image-multi-devices/test.sh
index d78c44fb..5430847f 100755
--- a/tests/misc-tests/021-image-multi-devices/test.sh
+++ b/tests/misc-tests/021-image-multi-devices/test.sh
@@ -20,31 +20,31 @@ loop2=$(run_check_stdout $SUDO_HELPER losetup --find --show dev2)
# Create the test file system.
-run_check $SUDO_HELPER "$TOP/mkfs.btrfs" -f $loop1 $loop2
-run_check $SUDO_HELPER mount $loop1 "$TEST_MNT"
-run_check $SUDO_HELPER dd bs=1M count=1 if=/dev/zero "of=$TEST_MNT/foobar"
+run_check $SUDO_HELPER "$TOP/mkfs.btrfs" -f "$loop1" "$loop2"
+run_check $SUDO_HELPER mount "$loop1" "$TEST_MNT"
+run_check $SUDO_HELPER dd bs=1M count=1 if=/dev/zero of="$TEST_MNT/foobar"
orig_md5=$(run_check_stdout md5sum "$TEST_MNT/foobar" | cut -d ' ' -f 1)
run_check $SUDO_HELPER umount "$TEST_MNT"
# Create the image to restore later.
-run_check $SUDO_HELPER "$TOP/btrfs-image" $loop1 "$IMAGE"
+run_check $SUDO_HELPER "$TOP/btrfs-image" "$loop1" "$IMAGE"
# Wipe out the filesystem from the devices, restore the image on a single
# device, check everything works and file foobar is there and with 1Mb of
# zeroes.
-run_check $SUDO_HELPER wipefs -a $loop1
-run_check $SUDO_HELPER wipefs -a $loop2
+run_check $SUDO_HELPER wipefs -a "$loop1"
+run_check $SUDO_HELPER wipefs -a "$loop2"
-run_check $SUDO_HELPER $TOP/btrfs-image -r "$IMAGE" $loop1
+run_check $SUDO_HELPER "$TOP/btrfs-image" -r "$IMAGE" "$loop1"
-run_check $SUDO_HELPER mount $loop1 "$TEST_MNT"
+run_check $SUDO_HELPER mount "$loop1" "$TEST_MNT"
new_md5=$(run_check_stdout md5sum "$TEST_MNT/foobar" | cut -d ' ' -f 1)
run_check $SUDO_HELPER umount "$TEST_MNT"
# Cleanup loop devices.
-run_check $SUDO_HELPER losetup -d $loop1
-run_check $SUDO_HELPER losetup -d $loop2
+run_check $SUDO_HELPER losetup -d "$loop1"
+run_check $SUDO_HELPER losetup -d "$loop2"
rm -f dev1 dev2
# Compare the file digests.
-[ $orig_md5 == $new_md5 ] || _fail "File digests do not match"
+[ "$orig_md5" == "$new_md5" ] || _fail "File digests do not match"
diff --git a/tests/misc-tests/023-device-usage-with-missing-device/test.sh b/tests/misc-tests/023-device-usage-with-missing-device/test.sh
index 05894cfe..57b500c9 100755
--- a/tests/misc-tests/023-device-usage-with-missing-device/test.sh
+++ b/tests/misc-tests/023-device-usage-with-missing-device/test.sh
@@ -12,7 +12,7 @@ setup_root_helper
test_run()
{
# empty filesystem, with enough redundancy so degraded mount works
- run_check $SUDO_HELPER "$TOP/mkfs.btrfs" -f -d raid1 -m raid1 $dev1 $dev2
+ run_check $SUDO_HELPER "$TOP/mkfs.btrfs" -f -d raid1 -m raid1 "$dev1" "$dev2"
TEST_DEV="$dev1"
run_check_mount_test_dev
@@ -21,8 +21,8 @@ test_run()
grep -q "slack.*16\\.00EiB" && _fail
run_check_umount_test_dev
- run_mayfail wipefs -a $dev2
- run_check $SUDO_HELPER losetup -d $dev2
+ run_mayfail wipefs -a "$dev2"
+ run_check $SUDO_HELPER losetup -d "$dev2"
unset loopdevs[2]
run_check_mount_test_dev -o degraded,ro
diff --git a/tests/misc-tests/024-inspect-internal-rootid/test.sh b/tests/misc-tests/024-inspect-internal-rootid/test.sh
index 71e19044..ea0c6298 100755
--- a/tests/misc-tests/024-inspect-internal-rootid/test.sh
+++ b/tests/misc-tests/024-inspect-internal-rootid/test.sh
@@ -36,15 +36,15 @@ id6=$(run_check_stdout "$TOP/btrfs" inspect-internal rootid dir/file2) \
id7=$(run_check_stdout "$TOP/btrfs" inspect-internal rootid sub/file3) \
|| { echo $id7; exit 1; }
-if ! ([ $id1 -ne $id2 ] && [ $id1 -ne $id3 ] && [ $id2 -ne $id3 ]); then
+if ! ([ "$id1" -ne "$id2" ] && [ "$id1" -ne "$id3" ] && [ "$id2" -ne "$id3" ]); then
_fail "inspect-internal rootid: each subvolume must have different id"
fi
-if ! ([ $id1 -eq $id4 ] && [ $id1 -eq $id5 ] && [ $id1 -eq $id6 ]); then
+if ! ([ "$id1" -eq "$id4" ] && [ "$id1" -eq "$id5" ] && [ "$id1" -eq "$id6" ]); then
_fail "inspect-internal rootid: rootid mismatch found"
fi
-if ! ([ $id2 -eq $id7 ]); then
+if ! ([ "$id2" -eq "$id7" ]); then
_fail "inspect-internal rootid: rootid mismatch found"
fi
diff --git a/tests/misc-tests/028-superblock-recover/test.sh b/tests/misc-tests/028-superblock-recover/test.sh
index 1175e480..ee117d0f 100755
--- a/tests/misc-tests/028-superblock-recover/test.sh
+++ b/tests/misc-tests/028-superblock-recover/test.sh
@@ -1,7 +1,7 @@
#!/bin/bash
# Test that any superblock is correctly detected and fixed by btrfs rescue
-source "$TOP/tests/common"
+source "$TEST_TOP/common"
check_prereq btrfs
check_prereq mkfs.btrfs
@@ -25,9 +25,9 @@ function check_corruption {
run_check $SUDO_HELPER dd bs=1K count=1 seek=$(($sb_offset + 1)) if=/dev/zero of="$TEST_DEV" conv=notrunc
# if corrupting one of the sb copies, copy it over the initial superblock
- if [ ! -z $source_sb ]; then
+ if [ ! -z "$source_sb" ]; then
local shift_val=$((16 << $source_sb * 12 ))
- run_check $SUDO_HELPER dd bs=1K count=4 seek=64 skip=$shift_val if="$TEST_DEV" of="$TEST_DEV" conv=notrunc
+ run_check $SUDO_HELPER dd bs=1K count=4 seek=64 skip="$shift_val" if="$TEST_DEV" of="$TEST_DEV" conv=notrunc
fi
# we can't use our mount helper, the following works for file image and
diff --git a/tests/misc-tests/029-send-p-different-mountpoints/test.sh b/tests/misc-tests/029-send-p-different-mountpoints/test.sh
new file mode 100755
index 00000000..0b42b772
--- /dev/null
+++ b/tests/misc-tests/029-send-p-different-mountpoints/test.sh
@@ -0,0 +1,51 @@
+#!/bin/bash
+# test that send -p does not corrupt paths when send is using 2 different mount
+# points
+
+source "$TEST_TOP/common"
+
+check_prereq btrfs
+check_prereq mkfs.btrfs
+
+setup_root_helper
+prepare_test_dev
+
+# we need two mount points, cannot nest the subvoolume under TEST_MNT
+SUBVOL_MNT="$TEST_MNT/subvol"
+TOPLEVEL_MNT="$TEST_MNT/toplevel"
+TEST_MNT="$TOPLEVEL_MNT"
+mkdir -p "$TOPLEVEL_MNT" "$SUBVOL_MNT"
+
+run_check $SUDO_HELPER "$TOP/mkfs.btrfs" -f "$TEST_DEV"
+run_check_mount_test_dev
+
+run_check $SUDO_HELPER "$TOP/btrfs" subvolume create "$TOPLEVEL_MNT/subv1"
+run_check $SUDO_HELPER mount -t btrfs -o subvol=subv1 "$TEST_DEV" "$SUBVOL_MNT"
+
+run_check $SUDO_HELPER "$TOP/btrfs" subvolume create "$TOPLEVEL_MNT/test-subvol"
+run_check $SUDO_HELPER "$TOP/btrfs" subvolume snapshot -r \
+ "$TOPLEVEL_MNT/test-subvol" "$SUBVOL_MNT/test-subvol-mnt-subvol"
+run_check $SUDO_HELPER "$TOP/btrfs" subvolume snapshot -r \
+ "$TOPLEVEL_MNT/test-subvol" "$TOPLEVEL_MNT/test-subvol-mnt-root"
+
+run_mustfail_stdout "send -p on 2 mount points" \
+ $SUDO_HELPER "$TOP/btrfs" send -f /dev/null -p \
+ "$SUBVOL_MNT/test-subvol-mnt-subvol" "$TOPLEVEL_MNT/test-subvol-mnt-root" \
+ | _log_stdout \
+ | grep -q "not on mount point.*/toplevel" \
+ || _fail "expected output not found, please check the logs"
+
+# without a fix, this leads to a corrupted path, with something like:
+#
+# ERROR: open st-subvol-mnt-subvol failed. No such file or directory
+# ^^^^^^^^^^^^^^^^^^^^
+# ERROR: could not resolve rootid for .../tests/mnt/subvol/test-subvol-mnt-subvol
+
+# expected output:
+# ERROR: not on mount point: .../tests/mnt/toplevel
+
+run_check_umount_test_dev "$SUBVOL_MNT"
+run_check_umount_test_dev "$TOPLEVEL_MNT"
+
+rmdir "$SUBVOL_MNT"
+rmdir "$TOPLEVEL_MNT"
diff --git a/tests/misc-tests/030-missing-device-image/test.sh b/tests/misc-tests/030-missing-device-image/test.sh
new file mode 100755
index 00000000..a04efe8f
--- /dev/null
+++ b/tests/misc-tests/030-missing-device-image/test.sh
@@ -0,0 +1,58 @@
+#!/bin/bash
+# Test that btrfs-image can dump image correctly for a missing device (RAID1)
+#
+# At least for RAID1, btrfs-image should be able to handle one missing device
+# without any problem
+
+source "$TEST_TOP/common"
+
+check_prereq btrfs-image
+check_prereq mkfs.btrfs
+check_prereq btrfs
+
+setup_root_helper
+setup_loopdevs 2
+prepare_loopdevs
+dev1=${loopdevs[1]}
+dev2=${loopdevs[2]}
+
+# $1: device number to remove (either 1 or 2)
+test_missing()
+{
+ local bad_num
+ local bad_dev
+ local good_num
+ local good_dev
+
+ bad_num=$1
+ bad_dev=${loopdevs[$bad_num]}
+ good_num=$((3 - $bad_num))
+ good_dev=${loopdevs[$good_num]}
+
+ run_check $SUDO_HELPER "$TOP/mkfs.btrfs" -f -d raid1 -m raid1 "$dev1" "$dev2"
+
+ # fill the fs with some data, we could create space cache
+ run_check $SUDO_HELPER mount "$dev1" "$TEST_MNT"
+ run_check $SUDO_HELPER dd if=/dev/zero of="$TEST_MNT/a" bs=1M count=10
+ run_check $SUDO_HELPER dd if=/dev/zero of="$TEST_MNT/b" bs=4k count=1000 conv=sync
+ run_check $SUDO_HELPER umount "$TEST_MNT"
+
+ # make sure we have space cache
+ if ! run_check_stdout "$TOP/btrfs" inspect dump-tree -t root "$dev1" |
+ grep -q "EXTENT_DATA"; then
+ # Normally the above operation should create the space cache.
+ # If not, it may mean we have migrated to v2 cache by default
+ _not_run "unable to create v1 space cache"
+ fi
+
+ # now wipe the device
+ run_check wipefs -fa "$bad_dev"
+
+ # we don't care about the image but btrfs-image must not fail
+ run_check "$TOP/btrfs-image" "$good_dev" /dev/null
+}
+
+# Test with either device missing, so we're ensured to hit missing device
+test_missing 1
+test_missing 2
+cleanup_loopdevs
diff --git a/tests/mkfs-tests/001-basic-profiles/test.sh b/tests/mkfs-tests/001-basic-profiles/test.sh
index b84016f7..6e295274 100755
--- a/tests/mkfs-tests/001-basic-profiles/test.sh
+++ b/tests/mkfs-tests/001-basic-profiles/test.sh
@@ -11,28 +11,27 @@ setup_root_helper
test_get_info()
{
- run_check $SUDO_HELPER $TOP/btrfs inspect-internal dump-super $dev1
- run_check $SUDO_HELPER $TOP/btrfs check $dev1
- run_check $SUDO_HELPER mount $dev1 $TEST_MNT
- run_check $TOP/btrfs filesystem df $TEST_MNT
- run_check $SUDO_HELPER $TOP/btrfs filesystem usage $TEST_MNT
- run_check $SUDO_HELPER $TOP/btrfs device usage $TEST_MNT
+ run_check $SUDO_HELPER "$TOP/btrfs" inspect-internal dump-super "$dev1"
+ run_check $SUDO_HELPER "$TOP/btrfs" check "$dev1"
+ run_check $SUDO_HELPER mount "$dev1" "$TEST_MNT"
+ run_check "$TOP/btrfs" filesystem df "$TEST_MNT"
+ run_check $SUDO_HELPER "$TOP/btrfs" filesystem usage "$TEST_MNT"
+ run_check $SUDO_HELPER "$TOP/btrfs" device usage "$TEST_MNT"
run_check $SUDO_HELPER umount "$TEST_MNT"
}
test_do_mkfs()
{
- run_check $SUDO_HELPER $TOP/mkfs.btrfs -f \
- $@
+ run_check $SUDO_HELPER "$TOP/mkfs.btrfs" -f "$@"
}
test_mkfs_single()
{
- test_do_mkfs $@ $dev1
+ test_do_mkfs "$@" "$dev1"
test_get_info
}
test_mkfs_multi()
{
- test_do_mkfs $@ ${loopdevs[@]}
+ test_do_mkfs "$@" ${loopdevs[@]}
test_get_info
}
diff --git a/tests/mkfs-tests/004-rootdir-keeps-size/test.sh b/tests/mkfs-tests/004-rootdir-keeps-size/test.sh
index 4a84e6db..635a5137 100755
--- a/tests/mkfs-tests/004-rootdir-keeps-size/test.sh
+++ b/tests/mkfs-tests/004-rootdir-keeps-size/test.sh
@@ -13,12 +13,12 @@ test_mkfs_with_size() {
local tmp
size="$1"
- run_check truncate -s$size $TEST_DEV
- imgsize=$(run_check_stdout stat --format=%s $TEST_DEV)
- run_check $SUDO_HELPER $TOP/mkfs.btrfs -f \
- --rootdir $INTERNAL_BIN/Documentation \
- $TEST_DEV
- tmp=$(run_check_stdout stat --format=%s $TEST_DEV)
+ run_check truncate -s"$size" "$TEST_DEV"
+ imgsize=$(run_check_stdout stat --format=%s "$TEST_DEV")
+ run_check $SUDO_HELPER "$TOP/mkfs.btrfs" -f \
+ --rootdir "$INTERNAL_BIN/Documentation" \
+ "$TEST_DEV"
+ tmp=$(run_check_stdout stat --format=%s "$TEST_DEV")
if ! [ "$imgsize" = "$tmp" ]; then
_fail "image size changed from $imgsize to $tmp"
fi
diff --git a/tests/mkfs-tests/005-long-device-name-for-ssd/test.sh b/tests/mkfs-tests/005-long-device-name-for-ssd/test.sh
index 55ce676e..b7c76b18 100755
--- a/tests/mkfs-tests/005-long-device-name-for-ssd/test.sh
+++ b/tests/mkfs-tests/005-long-device-name-for-ssd/test.sh
@@ -11,31 +11,31 @@ prepare_test_dev
# prep device
dmname=\
btrfs-test-with-very-long-name-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-dmdev=/dev/mapper/$dmname
+dmdev="/dev/mapper/$dmname"
run_check truncate -s0 img
chmod a+w img
run_check truncate -s2g img
loopdev=`run_check_stdout $SUDO_HELPER losetup --find --show img`
-run_check $SUDO_HELPER dmsetup create $dmname --table "0 1048576 linear $loopdev 0"
+run_check $SUDO_HELPER dmsetup create "$dmname" --table "0 1048576 linear $loopdev 0"
-dmbase=`readlink -f $dmdev`
+dmbase=`readlink -f "$dmdev"`
base=`basename "$dmbase"`
-rot=/sys/class/block/$base/queue/rotational
+rot="/sys/class/block/$base/queue/rotational"
# switch rotational
run_check cat $rot
-echo 0 | run_check $SUDO_HELPER tee $rot
-run_check cat $rot
+echo 0 | run_check $SUDO_HELPER tee "$rot"
+run_check cat "$rot"
# test
-run_check_stdout $SUDO_HELPER $TOP/mkfs.btrfs -f $@ $dmdev |
+run_check_stdout $SUDO_HELPER "$TOP/mkfs.btrfs" -f "$@" "$dmdev" |
grep -q 'SSD detected:.*yes' || _fail 'SSD not detected'
-run_check $SUDO_HELPER $TOP/btrfs inspect-internal dump-super $dmdev
+run_check $SUDO_HELPER "$TOP/btrfs" inspect-internal dump-super "$dmdev"
# cleanup
-run_check $SUDO_HELPER dmsetup remove $dmname
-run_mayfail $SUDO_HELPER losetup -d $loopdev
+run_check $SUDO_HELPER dmsetup remove "$dmname"
+run_mayfail $SUDO_HELPER losetup -d "$loopdev"
run_check truncate -s0 img
rm img
diff --git a/tests/mkfs-tests/006-partitioned-loopdev/test.sh b/tests/mkfs-tests/006-partitioned-loopdev/test.sh
index 06c254fd..5d972a78 100755
--- a/tests/mkfs-tests/006-partitioned-loopdev/test.sh
+++ b/tests/mkfs-tests/006-partitioned-loopdev/test.sh
@@ -18,15 +18,15 @@ cp partition-1g-1g img
run_check truncate -s2g img
loopdev=$(run_check_stdout $SUDO_HELPER losetup --partscan --find --show img)
-base=$(basename $loopdev)
+base=$(basename "$loopdev")
# expect partitions named like loop0p1 etc
-for looppart in $(ls /dev/$base?*); do
- run_check $SUDO_HELPER $TOP/mkfs.btrfs -f $looppart
- run_check $SUDO_HELPER $TOP/btrfs inspect-internal dump-super $looppart
+for looppart in $(ls /dev/"$base"?*); do
+ run_check $SUDO_HELPER "$TOP/mkfs.btrfs" -f "$looppart"
+ run_check $SUDO_HELPER "$TOP/btrfs" inspect-internal dump-super "$looppart"
done
# cleanup
-run_check $SUDO_HELPER losetup -d $loopdev
+run_check $SUDO_HELPER losetup -d "$loopdev"
run_check truncate -s0 img
rm img
diff --git a/tests/mkfs-tests/007-mix-nodesize-sectorsize/test.sh b/tests/mkfs-tests/007-mix-nodesize-sectorsize/test.sh
index f7e88c9a..31f44f33 100755
--- a/tests/mkfs-tests/007-mix-nodesize-sectorsize/test.sh
+++ b/tests/mkfs-tests/007-mix-nodesize-sectorsize/test.sh
@@ -11,9 +11,9 @@ prepare_test_dev
test_mkfs_single()
{
- run_check $SUDO_HELPER $TOP/mkfs.btrfs -f "$@" $TEST_DEV
- run_check $SUDO_HELPER $TOP/btrfs inspect-internal dump-super $TEST_DEV
- run_check $SUDO_HELPER $TOP/btrfs check $TEST_DEV
+ run_check $SUDO_HELPER "$TOP/mkfs.btrfs" -f "$@" "$TEST_DEV"
+ run_check $SUDO_HELPER "$TOP/btrfs" inspect-internal dump-super "$TEST_DEV"
+ run_check $SUDO_HELPER "$TOP/btrfs" check "$TEST_DEV"
}
# default
@@ -22,14 +22,14 @@ test_mkfs_single
# nodesize >= sectorsize
for nodesize in 4096 8192 16384 32768 65536; do
for sectorsize in 4096 8192 16384 32768 65536; do
- [ $nodesize -lt $sectorsize ] && continue
- test_mkfs_single -n $nodesize -s $sectorsize -d single -m single
- test_mkfs_single -n $nodesize -s $sectorsize -d single -m dup
+ [ "$nodesize" -lt "$sectorsize" ] && continue
+ test_mkfs_single -n "$nodesize" -s "$sectorsize" -d single -m single
+ test_mkfs_single -n "$nodesize" -s "$sectorsize" -d single -m dup
done
done
# nodesize, mixed mode
for nodesize in 4k 8k 16k 32k 64k; do
- test_mkfs_single -n $nodesize -s $nodesize -d single -m single --mixed
- test_mkfs_single -n $nodesize -s $nodesize -d dup -m dup --mixed
+ test_mkfs_single -n "$nodesize" -s "$nodesize" -d single -m single --mixed
+ test_mkfs_single -n "$nodesize" -s "$nodesize" -d dup -m dup --mixed
done
diff --git a/tests/mkfs-tests/008-sectorsize-nodesize-combination/test.sh b/tests/mkfs-tests/008-sectorsize-nodesize-combination/test.sh
index 9cc2f9e0..345d81de 100755
--- a/tests/mkfs-tests/008-sectorsize-nodesize-combination/test.sh
+++ b/tests/mkfs-tests/008-sectorsize-nodesize-combination/test.sh
@@ -19,13 +19,13 @@ do_test()
{
sectorsize=$1
nodesize=$2
- run_mayfail $TOP/mkfs.btrfs -f -O $features -n $nodesize -s $sectorsize \
- $TEST_DEV
+ run_mayfail "$TOP/mkfs.btrfs" -f -O "$features" -n "$nodesize" -s "$sectorsize" \
+ "$TEST_DEV"
ret=$?
- if [ $ret == 0 ]; then
- run_check $TOP/btrfs check $TEST_DEV
+ if [ "$ret" == 0 ]; then
+ run_check "$TOP/btrfs" check "$TEST_DEV"
fi
- return $ret
+ return "$ret"
}
# Invalid: Unaligned sectorsize and nodesize
diff --git a/tests/mkfs-tests/010-minimal-size/test.sh b/tests/mkfs-tests/010-minimal-size/test.sh
index 62f8e16a..0ebe0995 100755
--- a/tests/mkfs-tests/010-minimal-size/test.sh
+++ b/tests/mkfs-tests/010-minimal-size/test.sh
@@ -1,7 +1,7 @@
#!/bin/bash
# test if the reported minimal size of mkfs.btrfs is valid
-source "$TOP/tests/common"
+source "$TEST_TOP/common"
check_prereq mkfs.btrfs
check_prereq btrfs
@@ -14,12 +14,12 @@ do_test()
# the minimal device size for the given option combination
prepare_test_dev 1M
output=$(run_mustfail_stdout "mkfs.btrfs for small image" \
- "$TOP/mkfs.btrfs" -f $@ "$TEST_DEV")
+ "$TOP/mkfs.btrfs" -f "$@" "$TEST_DEV")
good_size=$(echo "$output" | grep -oP "(?<=is )\d+")
prepare_test_dev "$good_size"
echo "Minimal device size is $good_size" >> "$RESULTS"
- run_check $TOP/mkfs.btrfs -f $@ "$TEST_DEV"
+ run_check $TOP/mkfs.btrfs -f "$@" "$TEST_DEV"
run_check_mount_test_dev
run_check_umount_test_dev
}
diff --git a/tests/mkfs-tests/011-rootdir-create-file/test.sh b/tests/mkfs-tests/011-rootdir-create-file/test.sh
index c04f711d..20f7c4ce 100755
--- a/tests/mkfs-tests/011-rootdir-create-file/test.sh
+++ b/tests/mkfs-tests/011-rootdir-create-file/test.sh
@@ -3,7 +3,7 @@
# Expected behavior: it should create a new file if destination doesn't exist
# Regression 460e93f25754 ("btrfs-progs: mkfs: check the status of file at mkfs")
-source "$TOP/tests/common"
+source "$TEST_TOP/common"
check_prereq mkfs.btrfs
diff --git a/tests/mkfs-tests/012-rootdir-no-shrink/test.sh b/tests/mkfs-tests/012-rootdir-no-shrink/test.sh
index c1cb04f5..765f8cdd 100755
--- a/tests/mkfs-tests/012-rootdir-no-shrink/test.sh
+++ b/tests/mkfs-tests/012-rootdir-no-shrink/test.sh
@@ -1,7 +1,7 @@
#!/bin/bash
# Test if mkfs.btrfs --rootdir will skip shrinking correctly
-source "$TOP/tests/common"
+source "$TEST_TOP/common"
check_prereq mkfs.btrfs
@@ -11,7 +11,7 @@ fs_size=$((512 * 1024 * 1024))
bs=$((1024 * 1024))
tmp=$(mktemp -d --tmpdir btrfs-progs-mkfs.rootdirXXXXXXX)
-prepare_test_dev $fs_size
+prepare_test_dev "$fs_size"
# No shrink case
@@ -20,7 +20,7 @@ run_check_mount_test_dev
# We should be able to write at least half of the fs size data since the fs is
# not shrunk
-run_check $SUDO_HELPER dd if=/dev/zero bs=$bs count=$(($fs_size / $bs / 2)) \
+run_check $SUDO_HELPER dd if=/dev/zero bs="$bs" count=$(($fs_size / $bs / 2)) \
of="$TEST_MNT/file"
run_check_umount_test_dev
@@ -31,7 +31,7 @@ run_check "$TOP/mkfs.btrfs" -f --rootdir "$tmp" --shrink "$TEST_DEV"
run_check_mount_test_dev
run_mustfail "mkfs.btrfs for shrink rootdir" \
- $SUDO_HELPER dd if=/dev/zero bs=$bs count=$(($fs_size / $bs / 2)) \
+ $SUDO_HELPER dd if=/dev/zero bs="$bs" count=$(($fs_size / $bs / 2)) \
of="$TEST_MNT/file"
run_check_umount_test_dev
diff --git a/tests/mkfs-tests/013-reserved-1M-for-single/test.sh b/tests/mkfs-tests/013-reserved-1M-for-single/test.sh
index 1490df2c..1944ad93 100755
--- a/tests/mkfs-tests/013-reserved-1M-for-single/test.sh
+++ b/tests/mkfs-tests/013-reserved-1M-for-single/test.sh
@@ -6,7 +6,7 @@
# using btrfs_alloc_chunk() which won't use the 0~1M range, so other profiles
# are safe, but we test them nevertheless.
-source "$TOP/tests/common"
+source "$TEST_TOP/common"
check_prereq mkfs.btrfs
check_prereq btrfs
@@ -22,14 +22,14 @@ do_one_test ()
dump-tree -t device "$TEST_DEV" | \
grep -oP '(?<=DEV_EXTENT )[[:digit:]]*' | head -n1)
- if [ -z $first_dev_extent ]; then
+ if [ -z "$first_dev_extent" ]; then
_fail "failed to get first device extent"
fi
echo "first dev extent starts at $first_dev_extent" >> "$RESULTS"
echo "reserved range is [0, $(( 1024 * 1024)))" >> "$RESULTS"
# First device extent should not start below 1M
- if [ $first_dev_extent -lt $(( 1024 * 1024 )) ]; then
+ if [ "$first_dev_extent" -lt $(( 1024 * 1024 )) ]; then
_fail "first device extent occupies reserved 0~1M range"
fi
}
diff --git a/tests/mkfs-tests/014-rootdir-inline-extent/test.sh b/tests/mkfs-tests/014-rootdir-inline-extent/test.sh
new file mode 100755
index 00000000..09a96004
--- /dev/null
+++ b/tests/mkfs-tests/014-rootdir-inline-extent/test.sh
@@ -0,0 +1,46 @@
+#!/bin/bash
+# Regression test for mkfs.btrfs --rootdir with inline file extents
+# For any large inline file extent, btrfs check could already report it
+
+source "$TEST_TOP/common"
+
+check_prereq mkfs.btrfs
+
+prepare_test_dev
+
+tmp=$(mktemp -d --tmpdir btrfs-progs-mkfs.rootdirXXXXXXX)
+
+pagesize=$(getconf PAGESIZE)
+create_file()
+{
+ local size=$1
+ # Reuse size as filename
+ eval printf "%0.sx" {1..$size} > "$tmp/$size"
+}
+
+test_mkfs_rootdir()
+{
+ nodesize=$1
+ run_check "$TOP/mkfs.btrfs" --nodesize "$nodesize" -f --rootdir "$tmp" \
+ "$TEST_DEV"
+ run_check $SUDO_HELPER "$TOP/btrfs" check "$TEST_DEV"
+}
+
+# Use power of 2 from 512 to 64K (maximum node size) as base file size
+for i in 512 1024 2048 4096 8192 16384 32768; do
+ create_file $(($i - 102))
+ # 101 is the overhead size for max inline extent
+ create_file $(($i - 101))
+ create_file $(($i - 100))
+
+ create_file $(($i - 1))
+ create_file $i
+ create_file $(($i + 1))
+done
+
+for nodesize in 4096 8192 16384 32768 65536; do
+ if [ "$nodesize" -ge "$pagesize" ]; then
+ test_mkfs_rootdir "$nodesize"
+ fi
+done
+rm -rf -- "$tmp"
diff --git a/tests/mkfs-tests/015-fstree-uuid-otime/test.sh b/tests/mkfs-tests/015-fstree-uuid-otime/test.sh
new file mode 100755
index 00000000..a2e04a3f
--- /dev/null
+++ b/tests/mkfs-tests/015-fstree-uuid-otime/test.sh
@@ -0,0 +1,35 @@
+#!/bin/bash
+# verify that mkfs fills the uuid and otime for FS_TREE
+
+source "$TEST_TOP/common"
+
+check_prereq mkfs.btrfs
+check_prereq btrfs
+
+setup_root_helper
+prepare_test_dev
+
+# item 3 key (FS_TREE ROOT_ITEM 0) itemoff 14949 itemsize 439
+# generation 4 root_dirid 256 bytenr 30408704 level 0 refs 1
+# lastsnap 0 byte_limit 0 bytes_used 16384 flags 0x0(none)
+# uuid 322826f3-817a-4111-89ff-5481bfd516e2
+# ctime 1521656113.0 (2018-03-21 19:15:13)
+# otime 1521656113.0 (2018-03-21 19:15:13)
+# drop key (0 UNKNOWN.0 0) level 0
+
+run_check $SUDO_HELPER "$TOP/mkfs.btrfs" -f "$@" "$TEST_DEV"
+# match not-all-zeros in the first part
+uuid=$(run_check_stdout $SUDO_HELPER "$TOP/btrfs" inspect-internal dump-tree -t root "$TEST_DEV" | \
+ grep -A 3 "FS_TREE ROOT_ITEM 0" | grep 'uuid ')
+
+if [ $? != 0 ]; then
+ _fail "uuid for FS_TREE not found"
+fi
+
+if [ "$uuid" = '00000000-0000-0000-0000-000000000000' ]; then
+ _fail "uuid for FS_TREE is null"
+fi
+
+run_check_stdout $SUDO_HELPER "$TOP/btrfs" inspect-internal dump-tree -t root "$TEST_DEV" | \
+ grep -A 5 "FS_TREE ROOT_ITEM 0" | grep -q 'otime ' || \
+ _fail "otime for FS_TREE not found"
diff --git a/tests/mkfs-tests/016-rootdir-bad-symbolic-link/test.sh b/tests/mkfs-tests/016-rootdir-bad-symbolic-link/test.sh
new file mode 100755
index 00000000..39387ec7
--- /dev/null
+++ b/tests/mkfs-tests/016-rootdir-bad-symbolic-link/test.sh
@@ -0,0 +1,26 @@
+#!/bin/bash
+# Regression test for mkfs.btrfs --rootdir with dangling symlink (points to
+# non-existing location)
+#
+# Since mkfs.btrfs --rootdir will just create symbolic link rather than
+# follow it, we shouldn't hit any problem
+
+source "$TEST_TOP/common"
+
+check_prereq mkfs.btrfs
+prepare_test_dev
+
+tmp=$(mktemp -d --tmpdir btrfs-progs-mkfs.rootdirXXXXXXX)
+
+non_existing="/no/such/file$RANDOM$RANDOM"
+
+if [ -f "$non_existing" ]; then
+ _not_run "Some one created $non_existing, which is not expect to exist"
+fi
+
+run_check ln -sf "$non_existing" "$tmp/foobar"
+
+run_check "$TOP/mkfs.btrfs" -f --rootdir "$tmp" "$TEST_DEV"
+run_check "$TOP/btrfs" check "$TEST_DEV"
+
+rm -rf -- "$tmp"
diff --git a/travis/build-dep-reiserfs b/travis/build-dep-reiserfs
index b32b8058..72308ef2 100755
--- a/travis/build-dep-reiserfs
+++ b/travis/build-dep-reiserfs
@@ -2,14 +2,28 @@
# download, build and install reiserfs library
version=3.6.27
+dir=tmp-cached-reiser
+stamp="$dir/.last-build-reiser"
+here=`pwd`
set -e
-mkdir tmp-reiser
-cd tmp-reiser
+if [ -d "$dir" -a -f "$stamp" ]; then
+ echo "Using valid cache for $dir, built" `cat "$stamp"`
+ cd "$dir"
+ cd reiserfsprogs-${version}
+ sudo make install
+ exit 0
+fi
+
+echo "No or stale cache for $dir, rebuilding"
+rm -rf "$dir"
+mkdir "$dir"
+cd "$dir"
wget https://www.kernel.org/pub/linux/kernel/people/jeffm/reiserfsprogs/v${version}/reiserfsprogs-${version}.tar.xz
tar xf reiserfsprogs-${version}.tar.xz
cd reiserfsprogs-${version}
./configure --prefix=/usr
make all
sudo make install
+date > "$here/$stamp"
diff --git a/travis/build-dep-zstd b/travis/build-dep-zstd
index 22369dd3..eae0be63 100755
--- a/travis/build-dep-zstd
+++ b/travis/build-dep-zstd
@@ -2,13 +2,27 @@
# download, build and install the zstd library
version=1.3.3
+dir=tmp-cached-zstd
+stamp="$dir/.last-build-zstd"
+here=`pwd`
set -e
-mkdir tmp-zstd
-cd tmp-zstd
+if [ -d "$dir" -a -f "$stamp" ]; then
+ echo "Using valid cache for $dir, built" `cat "$stamp"`
+ cd "$dir"
+ cd zstd-${version}
+ sudo make install PREFIX=/usr
+ exit 0
+fi
+
+echo "No or stale cache for $dir, rebuilding"
+rm -rf "$dir"
+mkdir "$dir"
+cd "$dir"
wget https://github.com/facebook/zstd/archive/v${version}.tar.gz
tar xf v${version}.tar.gz
cd zstd-${version}
make
sudo make install PREFIX=/usr
+date > "$here/$stamp"
diff --git a/travis/images/ci-musl-x86_64/Dockerfile b/travis/images/ci-musl-x86_64/Dockerfile
index dbbe4dc1..15d59451 100644
--- a/travis/images/ci-musl-x86_64/Dockerfile
+++ b/travis/images/ci-musl-x86_64/Dockerfile
@@ -5,6 +5,7 @@ WORKDIR /tmp
RUN apk update
RUN apk add linux-headers musl-dev util-linux-dev bash
RUN apk add attr-dev acl-dev e2fsprogs-dev zlib-dev lzo-dev zstd-dev
-RUN apk add autoconf automake make gcc tar gzip
+RUN apk add autoconf automake make gcc tar gzip clang
+RUN apk add python3 py3-setuptools python3-dev
COPY ./test-build .
diff --git a/utils.c b/utils.c
index e9cb3a82..d81d4980 100644
--- a/utils.c
+++ b/utils.c
@@ -40,6 +40,8 @@
#include <linux/magic.h>
#include <getopt.h>
+#include <btrfsutil.h>
+
#include "kerncompat.h"
#include "radix-tree.h"
#include "ctree.h"
@@ -1048,7 +1050,7 @@ int btrfs_device_already_in_root(struct btrfs_root *root, int fd,
* structures.
*/
if (btrfs_super_magic(disk_super) != BTRFS_MAGIC &&
- btrfs_super_magic(disk_super) != BTRFS_MAGIC_PARTIAL)
+ btrfs_super_magic(disk_super) != BTRFS_MAGIC_TEMPORARY)
goto brelse;
if (!memcmp(disk_super->fsid, root->fs_info->super_copy->fsid,
@@ -1453,6 +1455,7 @@ u64 parse_qgroupid(const char *p)
char *s = strchr(p, '/');
const char *ptr_src_end = p + strlen(p);
char *ptr_parse_end = NULL;
+ enum btrfs_util_error err;
u64 level;
u64 id;
int fd;
@@ -1480,8 +1483,8 @@ u64 parse_qgroupid(const char *p)
path:
/* Path format like subv at 'my_subvol' is the fallback case */
- ret = test_issubvolume(p);
- if (ret < 0 || !ret)
+ err = btrfs_util_is_subvolume(p);
+ if (err)
goto err;
fd = open(p, O_RDONLY);
if (fd < 0)
@@ -2451,163 +2454,23 @@ int test_issubvolname(const char *name)
strcmp(name, ".") && strcmp(name, "..");
}
-/*
- * Test if path is a subvolume
- * Returns:
- * 0 - path exists but it is not a subvolume
- * 1 - path exists and it is a subvolume
- * < 0 - error
- */
-int test_issubvolume(const char *path)
-{
- struct stat st;
- struct statfs stfs;
- int res;
-
- res = stat(path, &st);
- if (res < 0)
- return -errno;
-
- if (st.st_ino != BTRFS_FIRST_FREE_OBJECTID || !S_ISDIR(st.st_mode))
- return 0;
-
- res = statfs(path, &stfs);
- if (res < 0)
- return -errno;
-
- return (int)stfs.f_type == BTRFS_SUPER_MAGIC;
-}
-
const char *subvol_strip_mountpoint(const char *mnt, const char *full_path)
{
int len = strlen(mnt);
if (!len)
return full_path;
+ if ((strncmp(mnt, full_path, len) != 0) || (full_path[len] != '/')) {
+ error("not on mount point: %s", mnt);
+ exit(1);
+ }
+
if (mnt[len - 1] != '/')
len += 1;
return full_path + len;
}
-/*
- * Returns
- * <0: Std error
- * 0: All fine
- * 1: Error; and error info printed to the terminal. Fixme.
- * 2: If the fullpath is root tree instead of subvol tree
- */
-int get_subvol_info(const char *fullpath, struct root_info *get_ri)
-{
- u64 sv_id;
- int ret = 1;
- int fd = -1;
- int mntfd = -1;
- char *mnt = NULL;
- const char *svpath = NULL;
- DIR *dirstream1 = NULL;
- DIR *dirstream2 = NULL;
-
- ret = test_issubvolume(fullpath);
- if (ret < 0)
- return ret;
- if (!ret) {
- error("not a subvolume: %s", fullpath);
- return 1;
- }
-
- ret = find_mount_root(fullpath, &mnt);
- if (ret < 0)
- return ret;
- if (ret > 0) {
- error("%s doesn't belong to btrfs mount point", fullpath);
- return 1;
- }
- ret = 1;
- svpath = subvol_strip_mountpoint(mnt, fullpath);
-
- fd = btrfs_open_dir(fullpath, &dirstream1, 1);
- if (fd < 0)
- goto out;
-
- ret = btrfs_list_get_path_rootid(fd, &sv_id);
- if (ret)
- goto out;
-
- mntfd = btrfs_open_dir(mnt, &dirstream2, 1);
- if (mntfd < 0)
- goto out;
-
- memset(get_ri, 0, sizeof(*get_ri));
- get_ri->root_id = sv_id;
-
- if (sv_id == BTRFS_FS_TREE_OBJECTID)
- ret = btrfs_get_toplevel_subvol(mntfd, get_ri);
- else
- ret = btrfs_get_subvol(mntfd, get_ri);
- if (ret)
- error("can't find '%s': %d", svpath, ret);
-
-out:
- close_file_or_dir(mntfd, dirstream2);
- close_file_or_dir(fd, dirstream1);
- free(mnt);
-
- return ret;
-}
-
-int get_subvol_info_by_rootid(const char *mnt, struct root_info *get_ri, u64 r_id)
-{
- int fd;
- int ret;
- DIR *dirstream = NULL;
-
- fd = btrfs_open_dir(mnt, &dirstream, 1);
- if (fd < 0)
- return -EINVAL;
-
- memset(get_ri, 0, sizeof(*get_ri));
- get_ri->root_id = r_id;
-
- if (r_id == BTRFS_FS_TREE_OBJECTID)
- ret = btrfs_get_toplevel_subvol(fd, get_ri);
- else
- ret = btrfs_get_subvol(fd, get_ri);
-
- if (ret)
- error("can't find rootid '%llu' on '%s': %d", r_id, mnt, ret);
-
- close_file_or_dir(fd, dirstream);
-
- return ret;
-}
-
-int get_subvol_info_by_uuid(const char *mnt, struct root_info *get_ri, u8 *uuid_arg)
-{
- int fd;
- int ret;
- DIR *dirstream = NULL;
-
- fd = btrfs_open_dir(mnt, &dirstream, 1);
- if (fd < 0)
- return -EINVAL;
-
- memset(get_ri, 0, sizeof(*get_ri));
- uuid_copy(get_ri->uuid, uuid_arg);
-
- ret = btrfs_get_subvol(fd, get_ri);
- if (ret) {
- char uuid_parsed[BTRFS_UUID_UNPARSED_SIZE];
- uuid_unparse(uuid_arg, uuid_parsed);
- error("can't find uuid '%s' on '%s': %d",
- uuid_parsed, mnt, ret);
- }
-
- close_file_or_dir(fd, dirstream);
-
- return ret;
-}
-
/* Set the seed manually */
void init_rand_seed(u64 seed)
{
diff --git a/utils.h b/utils.h
index b871c9ff..403de481 100644
--- a/utils.h
+++ b/utils.h
@@ -149,15 +149,9 @@ u64 disk_size(const char *path);
u64 get_partition_size(const char *dev);
int test_issubvolname(const char *name);
-int test_issubvolume(const char *path);
int test_isdir(const char *path);
const char *subvol_strip_mountpoint(const char *mnt, const char *full_path);
-int get_subvol_info(const char *fullpath, struct root_info *get_ri);
-int get_subvol_info_by_rootid(const char *mnt, struct root_info *get_ri,
- u64 rootid_arg);
-int get_subvol_info_by_uuid(const char *mnt, struct root_info *get_ri,
- u8 *uuid_arg);
int find_next_key(struct btrfs_path *path, struct btrfs_key *key);
const char* btrfs_group_type_str(u64 flag);
const char* btrfs_group_profile_str(u64 flag);
diff --git a/volumes.c b/volumes.c
index edad367b..c6e34321 100644
--- a/volumes.c
+++ b/volumes.c
@@ -30,6 +30,72 @@
#include "utils.h"
#include "kernel-lib/raid56.h"
+const struct btrfs_raid_attr btrfs_raid_array[BTRFS_NR_RAID_TYPES] = {
+ [BTRFS_RAID_RAID10] = {
+ .sub_stripes = 2,
+ .dev_stripes = 1,
+ .devs_max = 0, /* 0 == as many as possible */
+ .devs_min = 4,
+ .tolerated_failures = 1,
+ .devs_increment = 2,
+ .ncopies = 2,
+ },
+ [BTRFS_RAID_RAID1] = {
+ .sub_stripes = 1,
+ .dev_stripes = 1,
+ .devs_max = 2,
+ .devs_min = 2,
+ .tolerated_failures = 1,
+ .devs_increment = 2,
+ .ncopies = 2,
+ },
+ [BTRFS_RAID_DUP] = {
+ .sub_stripes = 1,
+ .dev_stripes = 2,
+ .devs_max = 1,
+ .devs_min = 1,
+ .tolerated_failures = 0,
+ .devs_increment = 1,
+ .ncopies = 2,
+ },
+ [BTRFS_RAID_RAID0] = {
+ .sub_stripes = 1,
+ .dev_stripes = 1,
+ .devs_max = 0,
+ .devs_min = 2,
+ .tolerated_failures = 0,
+ .devs_increment = 1,
+ .ncopies = 1,
+ },
+ [BTRFS_RAID_SINGLE] = {
+ .sub_stripes = 1,
+ .dev_stripes = 1,
+ .devs_max = 1,
+ .devs_min = 1,
+ .tolerated_failures = 0,
+ .devs_increment = 1,
+ .ncopies = 1,
+ },
+ [BTRFS_RAID_RAID5] = {
+ .sub_stripes = 1,
+ .dev_stripes = 1,
+ .devs_max = 0,
+ .devs_min = 2,
+ .tolerated_failures = 1,
+ .devs_increment = 1,
+ .ncopies = 2,
+ },
+ [BTRFS_RAID_RAID6] = {
+ .sub_stripes = 1,
+ .dev_stripes = 1,
+ .devs_max = 0,
+ .devs_min = 3,
+ .tolerated_failures = 2,
+ .devs_increment = 1,
+ .ncopies = 3,
+ },
+};
+
struct stripe {
struct btrfs_device *dev;
u64 physical;
@@ -54,14 +120,22 @@ static inline int nr_data_stripes(struct map_lookup *map)
static LIST_HEAD(fs_uuids);
-static struct btrfs_device *__find_device(struct list_head *head, u64 devid,
- u8 *uuid)
+/*
+ * Find a device specified by @devid or @uuid in the list of @fs_devices, or
+ * return NULL.
+ *
+ * If devid and uuid are both specified, the match must be exact, otherwise
+ * only devid is used.
+ */
+static struct btrfs_device *find_device(struct btrfs_fs_devices *fs_devices,
+ u64 devid, u8 *uuid)
{
+ struct list_head *head = &fs_devices->devices;
struct btrfs_device *dev;
list_for_each_entry(dev, head, dev_list) {
if (dev->devid == devid &&
- !memcmp(dev->uuid, uuid, BTRFS_UUID_SIZE)) {
+ (!uuid || !memcmp(dev->uuid, uuid, BTRFS_UUID_SIZE))) {
return dev;
}
}
@@ -100,7 +174,7 @@ static int device_list_add(const char *path,
fs_devices->lowest_devid = (u64)-1;
device = NULL;
} else {
- device = __find_device(&fs_devices->devices, devid,
+ device = find_device(fs_devices, devid,
disk_super->dev_item.uuid);
}
if (!device) {
@@ -450,10 +524,10 @@ out:
}
static int find_free_dev_extent(struct btrfs_device *device, u64 num_bytes,
- u64 *start)
+ u64 *start, u64 *len)
{
/* FIXME use last free of some kind */
- return find_free_dev_extent_start(device, num_bytes, 0, start, NULL);
+ return find_free_dev_extent_start(device, num_bytes, 0, start, len);
}
static int btrfs_alloc_dev_extent(struct btrfs_trans_handle *trans,
@@ -477,7 +551,7 @@ static int btrfs_alloc_dev_extent(struct btrfs_trans_handle *trans,
* is responsible to make sure it's free.
*/
if (!convert) {
- ret = find_free_dev_extent(device, num_bytes, start);
+ ret = find_free_dev_extent(device, num_bytes, start, NULL);
if (ret)
goto err;
}
@@ -826,7 +900,7 @@ error:
return ret;
}
-#define BTRFS_MAX_DEVS(r) ((BTRFS_LEAF_DATA_SIZE(r->fs_info) \
+#define BTRFS_MAX_DEVS(info) ((BTRFS_LEAF_DATA_SIZE(info) \
- sizeof(struct btrfs_item) \
- sizeof(struct btrfs_chunk)) \
/ sizeof(struct btrfs_stripe) + 1)
@@ -882,12 +956,12 @@ int btrfs_alloc_chunk(struct btrfs_trans_handle *trans,
calc_size = SZ_1G;
max_chunk_size = 10 * calc_size;
min_stripe_size = SZ_64M;
- max_stripes = BTRFS_MAX_DEVS(chunk_root);
+ max_stripes = BTRFS_MAX_DEVS(info);
} else if (type & BTRFS_BLOCK_GROUP_METADATA) {
calc_size = SZ_1G;
max_chunk_size = 4 * calc_size;
min_stripe_size = SZ_32M;
- max_stripes = BTRFS_MAX_DEVS(chunk_root);
+ max_stripes = BTRFS_MAX_DEVS(info);
}
}
if (type & BTRFS_BLOCK_GROUP_RAID1) {
@@ -1616,8 +1690,7 @@ struct btrfs_device *btrfs_find_device(struct btrfs_fs_info *fs_info, u64 devid,
if (!fsid ||
(!memcmp(cur_devices->fsid, fsid, BTRFS_UUID_SIZE) ||
fs_info->ignore_fsid_mismatch)) {
- device = __find_device(&cur_devices->devices,
- devid, uuid);
+ device = find_device(cur_devices, devid, uuid);
if (device)
return device;
}
diff --git a/volumes.h b/volumes.h
index 11572e78..5bcaae7f 100644
--- a/volumes.h
+++ b/volumes.h
@@ -108,6 +108,36 @@ struct map_lookup {
struct btrfs_bio_stripe stripes[];
};
+struct btrfs_raid_attr {
+ int sub_stripes; /* sub_stripes info for map */
+ int dev_stripes; /* stripes per dev */
+ int devs_max; /* max devs to use */
+ int devs_min; /* min devs needed */
+ int tolerated_failures; /* max tolerated fail devs */
+ int devs_increment; /* ndevs has to be a multiple of this */
+ int ncopies; /* how many copies to data has */
+};
+
+extern const struct btrfs_raid_attr btrfs_raid_array[BTRFS_NR_RAID_TYPES];
+
+static inline enum btrfs_raid_types btrfs_bg_flags_to_raid_index(u64 flags)
+{
+ if (flags & BTRFS_BLOCK_GROUP_RAID10)
+ return BTRFS_RAID_RAID10;
+ else if (flags & BTRFS_BLOCK_GROUP_RAID1)
+ return BTRFS_RAID_RAID1;
+ else if (flags & BTRFS_BLOCK_GROUP_DUP)
+ return BTRFS_RAID_DUP;
+ else if (flags & BTRFS_BLOCK_GROUP_RAID0)
+ return BTRFS_RAID_RAID0;
+ else if (flags & BTRFS_BLOCK_GROUP_RAID5)
+ return BTRFS_RAID_RAID5;
+ else if (flags & BTRFS_BLOCK_GROUP_RAID6)
+ return BTRFS_RAID_RAID6;
+
+ return BTRFS_RAID_SINGLE; /* BTRFS_BLOCK_GROUP_SINGLE */
+}
+
#define btrfs_multi_bio_size(n) (sizeof(struct btrfs_multi_bio) + \
(sizeof(struct btrfs_bio_stripe) * (n)))
#define btrfs_map_lookup_size(n) (sizeof(struct map_lookup) + \
@@ -179,6 +209,28 @@ static inline int check_crossing_stripes(struct btrfs_fs_info *fs_info,
(bg_offset + len - 1) / BTRFS_STRIPE_LEN);
}
+static inline u64 calc_stripe_length(u64 type, u64 length, int num_stripes)
+{
+ u64 stripe_size;
+
+ if (type & BTRFS_BLOCK_GROUP_RAID0) {
+ stripe_size = length;
+ stripe_size /= num_stripes;
+ } else if (type & BTRFS_BLOCK_GROUP_RAID10) {
+ stripe_size = length * 2;
+ stripe_size /= num_stripes;
+ } else if (type & BTRFS_BLOCK_GROUP_RAID5) {
+ stripe_size = length;
+ stripe_size /= (num_stripes - 1);
+ } else if (type & BTRFS_BLOCK_GROUP_RAID6) {
+ stripe_size = length;
+ stripe_size /= (num_stripes - 2);
+ } else {
+ stripe_size = length;
+ }
+ return stripe_size;
+}
+
int __btrfs_map_block(struct btrfs_fs_info *fs_info, int rw,
u64 logical, u64 *length, u64 *type,
struct btrfs_multi_bio **multi_ret, int mirror_num,