diff options
author | Dimitri John Ledkov <xnox@ubuntu.com> | 2017-02-13 11:24:33 +0000 |
---|---|---|
committer | Dimitri John Ledkov <xnox@ubuntu.com> | 2017-02-13 11:24:33 +0000 |
commit | 4305d024938113df5d73021a09eb2a991f54ca2f (patch) | |
tree | d9e7ecc9db14bcc1394607a9e6c644a8b93e9bea | |
parent | e693f0e4ffb1776a05b78264ee3d93d5f07efede (diff) |
New upstream release Closes: #849353, #817806, #854915, #845473
193 files changed, 9842 insertions, 4127 deletions
@@ -64,6 +64,7 @@ config/py-compile config/test-driver configure cscope.out +.clang_complete depcomp libtool Makefile diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..15313df4 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,67 @@ +# +# Copyright (C) 2016 Roman Lebedev. +# +# 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. +# + +sudo: required + +language: c +compiler: + - gcc + - clang + +cache: ccache + +git: + depth: 2 + +dist: trusty +group: unstable + +branches: + only: + - devel + - coverity_scan + - master + - release-test + +env: + global: + # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created + # via the "travis encrypt" command using the project repo's public key + - secure: "aOqVfsh0m0iR3ggfuqhri4RHZK1LkQPDfNvPPQLN6VwJnz9QrI7TvLovTM1JwfX+nkyNhRq33+OahQb7PEHWPctFo6o42CcN837syF2fg8ZhTmcU1abJL29GUN/bH3xZXJJoUNJUW79Bp098GEcEmypxJ29Fxs5FQKT57O2FnnJXQLZhsDvSC1tuHtHEnCU1EGmZ6g4QI+eaS3zxKr343WCwRX6xolKHaLZX/UsYMbIMJ1YBaK2zCyOpaXOflQbloI1gcrUcoalIFuwPVbYnu2oXqzsuzHV0ekN+zMAECDNYrTr/OEA0bLR57WC0krLiAr+tA6Rq5E1D6JHg3WxDE7tbuFmrhxW23S9x6xw4+L5KuwNdsIZEdybn4q6zCkkHH3PgOFRF8taxxHKsfJ9fWxZM/kvQa5CNClDrZmfso9U8yWrYgL6fi3fIcuVLE29N2K0v9LkWlsK0REn5/uiEKO5rJ25ytpzCoUg9IRRgMPJaoPtZhSrK+ywZlJqg3f5eRqA7W1A4AEK5cOmWGFW2MOGmTtzhHG/xY0yaPYxgB7u2b7ji8BQdOn3p0ttmBFBxbDChb3LUH+d21iORrYVTG3IDziQTdBLCn/ZUypcLlLXLkkgZMQ9kLhArRmuqlTqPoR2+GNVjRP/uxwZXcszvEb0TATI11rieJW6TN1inWgY=" + +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 + - echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca- + +addons: + coverity_scan: + project: + name: "kdave/btrfs-progs" + description: "btrfs-progs" + notification_email: dsterba@suse.cz + build_command_prepend: "./autogen.sh && ./configure --disable-documentation" + build_command: "make" + branch_pattern: coverity_scan + +script: + - "./autogen.sh && ./configure --disable-documentation && make" + - "make TEST_LOG=dump test-cli" + - "make TEST_LOG=dump test-mkfs" + - "make TEST_LOG=dump test-check" + - "make TEST_LOG=dump test-misc" @@ -1,3 +1,113 @@ +btrfs-progs-4.9.1 (2017-01-27) + * check: + * use correct inode number for lost+found files + * lowmem mode: fix false alert on dropped leaf + * size reports: negative numbers might appear in size reports during device + deletes (previously in EiB units) + * mkfs: print device being trimmed + * defrag: v1 ioctl support dropped + * quota: print message before starting to wait for rescan + * qgroup show: new option to sync before printing the stats + * other: + * corrupt-block enhancements + * backtrace and co. cleanups + * doc fixes + +btrfs-progs-4.9 (2016-12-23) + * check: many lowmem mode updates + * send: use splice syscall to copy buffer from kernel + * receive: new option to dump the stream in textual form + * convert: + * move sources to own directory + * prevent accounting of blocks beyond end of the device + * make it work with 64k sectorsize + * mkfs: move sources to own directory + * defrag: warns if directory used without -r + * dev stats: + * new option to check stats for non-zero values + * add long option for -z + * library: version bump to 0.1.2, added subvol_uuid_search2 + * other: + * cleanups + * docs updates + +btrfs-progs-4.8.5 (2016-11-30) + * receive: fix detection of end of stream (error reported even for valid + streams) + * other: + * added test for the receive bug + * fix linking of library-test + +btrfs-progs-4.8.4 (2016-11-25) + * check: support for clearing space cache v2 (free-space-tree) + * send: + * more sanity checks (with tests), cleanups + * fix for fstests/btrfs/038 and btrfs/117 failures + * build: + * fix compilation of standalone ioctl.h, pull NULL definition + * fix library link errors introduced in 4.8.3 + * tests: + * add more fuzzed images from bugzilla + * add bogus send stream checks + * fixups and enhancements for CI environment builds + * misc refinements and updates of testing framework + * other: + * move sources for btrfs-image to own directory + * deprecated and not build by default: btrfs-calc-size, btrfs-show-super + * docs updates + +btrfs-progs-4.8.3 (2016-11-11) + * check: + * support for clearing space cache (v1) + * size reduction of inode backref structure + * send: + * fix handling of multiple snapshots (-p and -c options) + * transfer buffer increased (should reduce number of context switches) + * reuse existing file for output (-f), eg. when root cannot create files (NFS) + * dump-tree: + * print missing items for various structures + * new: dev stats, balance status item + * sync key names with kernel (the persistent items) + * subvol show: now able to print the toplevel subvolume -- the creation time + might be wrong though + * mkfs: + * store the creation time of toplevel root inode + * print UUID in the summary + * build: travis CI for devel + * other: + * lots of cleanups and refactoring + * switched to on-stack path structure + * fixes from coverity, asan, ubsan + * new tests + * updates in testing infrastructure + * fixed convert test 005 + +btrfs-progs-4.8.2 (2016-10-26) + * convert: also convert file attributes + * convert: fix wrong tree block alignment for unalianged block group + * check: quota verify fixes, handle reloc tree + * build: add stub for FIEMAP_EXTENT_SHARED, compiles on ancient kernels + * build: add stub for BUILD_ASSERT when ioctl.h is included + * dump-tree: don't crash on unrecognized tree id for -t + * tests: + * add more ioctl tests + * convert: more symlink tests, attribute tests + * quota verify for reloc tree + * other cleanups + +btrfs-progs-4.8.1 (2016-10-12) + * 32bit builds fixed + * build without backtrace support fixed + +btrfs-progs-4.8 (2016-10-05) + * error handling improvements all over the place + * new fuzzed images, test updates + * doc fixups + * minor cleanups and improvements + * kernel library helpers moved to own directory + * qgroup: fix regression leading to incorrect status after check, + introduced in 4.7 + btrfs-progs-4.7.3 (2016-09-21) * fixed free space tree compat status * check: low-mem mode: handle partially dropped snapshots diff --git a/Documentation/ReleaseChecklist b/Documentation/ReleaseChecklist new file mode 100644 index 00000000..d8bf50c1 --- /dev/null +++ b/Documentation/ReleaseChecklist @@ -0,0 +1,37 @@ +Release checklist +================= + +Last code touches: + +* make the code ready, collect patches queued for the release +* look to mailinglist for any relevant last-minute fixes + +Pre-checks: + +* update package in OBS, (multi arch build checks) +* submit sources to coverity +* run all internal functional tests + * defaults + * D=asan + * D=ubsan +* run all build tests +* run with fstests + +Pre-release: + +* write CHANGES entry + +Release: + +* tag release, sign +* make tar +* build check of unpacked tar +* upload tar to kernel.org +* refresh git branches, push tags + +Post-release: + +* write and send announcement mail to mailinglist +* update wiki://Main_page#News +* update wiki://Changelog#btrfs-progs +* update title on IRC diff --git a/Documentation/btrfs-check.asciidoc b/Documentation/btrfs-check.asciidoc index abc9f0dc..633cbbf6 100644 --- a/Documentation/btrfs-check.asciidoc +++ b/Documentation/btrfs-check.asciidoc @@ -20,7 +20,7 @@ by the option '--readonly'. *btrfsck* is an alias of *btrfs check* command and is now deprecated. -WARNING: Do not use '--repair' unless you are adviced to by a developer, an +WARNING: Do not use '--repair' unless you are advised to by a developer, an experienced user or accept the fact that 'fsck' cannot possibly fix all sorts of damage that could happen to a filesystem because of software and hardware bugs. @@ -78,6 +78,20 @@ respective superblock offset is within the device size This can be used to use a different starting point if some of the primary superblock is damaged. +--clear-space-cache v1|v2:: +completely wipe all free space cache of given type ++ +For free space cache 'v1', the 'clear_cache' kernel mount option only rebuilds +the free space cache for block groups that are modified while the filesystem is +mounted with that option. Thus, using this option with 'v1' makes it possible +to actually clear the entire free space cache. ++ +For free space cache 'v2', the 'clear_cache' kernel mount option does destroy +the entire free space cache. This option with 'v2' provides an alternative +method of clearing the free space cache that doesn't require mounting the +filesystem. + + DANGEROUS OPTIONS ----------------- diff --git a/Documentation/btrfs-convert.asciidoc b/Documentation/btrfs-convert.asciidoc index ecc157cd..cbc1c730 100644 --- a/Documentation/btrfs-convert.asciidoc +++ b/Documentation/btrfs-convert.asciidoc @@ -33,6 +33,9 @@ have supported data block size (ie. the same that would be valid for 'mkfs.btrfs'). This is typically the system page size (4KiB on x86_64 machines). +NOTE: The source filesystem should be clean, you are encouraged to run the +'fsck' tool if you're not sure. + **REMOVE THE ORIGINAL FILESYSTEM METADATA** By removing the 'ext2_saved' subvolume, all metadata of the original filesystem diff --git a/Documentation/btrfs-device.asciidoc b/Documentation/btrfs-device.asciidoc index d05fc457..eedcac85 100644 --- a/Documentation/btrfs-device.asciidoc +++ b/Documentation/btrfs-device.asciidoc @@ -27,7 +27,7 @@ The device management works on a mounted filesystem. Devices can be added, removed or replaced, by commands profided by *btrfs device* and *btrfs replace*. The profiles can be also changed, provided there's enough workspace to do the -conversion, using the *btrfs balance* comand and namely the filter 'convert'. +conversion, using the *btrfs balance* command and namely the filter 'convert'. Profile:: A profile describes an allocation policy based on the redundancy/replication @@ -98,16 +98,22 @@ remain as such. Reloading the kernel module will drop this information. There's an alternative way of mounting multiple-device filesystem without the need for prior scanning. See the mount option 'device'. -*stats* [-z] <path>|<device>:: +*stats* [options] <path>|<device>:: Read and print the device IO error statistics for all devices of the given -filesystem identified by <path> or for a single <device>. See section *DEVICE -STATS* for more information. +filesystem identified by <path> or for a single <device>. The filesystem must +be mounted. See section *DEVICE STATS* for more information about the reported +statistics and the meaning. + `Options` + --z:::: +-z|--reset:::: Print the stats and reset the values to zero afterwards. +-c|--check:::: +Check if the stats are all zeros and return 0 it 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. + *usage* [options] <path> [<path>...]:: Show detailed information about internal allocations in devices. + @@ -180,7 +186,7 @@ logial mappings). What changed: -* available data space decreased by 3GiB, usable rougly (50 - 3) + (100 - 3) = 144 GiB +* available data space decreased by 3GiB, usable roughly (50 - 3) + (100 - 3) = 144 GiB * metadata redundancy increased IOW, the unequal device sizes allow for combined space for data yet improved @@ -231,6 +237,9 @@ EXIT STATUS *btrfs device* returns a zero exit status if it succeeds. Non zero is returned in case of failure. +If the '-s' option is used, *btrfs device stats* will add 64 to the +exit status if any of the error counters is non-zero. + AVAILABILITY ------------ *btrfs* is part of btrfs-progs. diff --git a/Documentation/btrfs-filesystem.asciidoc b/Documentation/btrfs-filesystem.asciidoc index 9782af9b..0f7ea495 100644 --- a/Documentation/btrfs-filesystem.asciidoc +++ b/Documentation/btrfs-filesystem.asciidoc @@ -80,7 +80,7 @@ show sizes in TiB, or TB with --si If conflicting options are passed, the last one takes precedence. *defragment* [options] <file>|<dir> [<file>|<dir>...]:: -Defragment file data on a mounted filesystem. +Defragment file data on a mounted filesystem. Requires kernel 2.6.33 and newer. + If '-r' is passed, files in dir will be defragmented recursively. The start position and the number of bytes to defragment can be specified by diff --git a/Documentation/btrfs-man5.asciidoc b/Documentation/btrfs-man5.asciidoc index caa9390b..acc4e429 100644 --- a/Documentation/btrfs-man5.asciidoc +++ b/Documentation/btrfs-man5.asciidoc @@ -319,25 +319,32 @@ May be resumed with *btrfs balance resume* or the paused state can be removed by *btrfs balance cancel*. The default behaviour is to start interrutpd balance. *space_cache*:: -*space_cache=v2*:: +*space_cache='version'*:: *nospace_cache*:: -('nospace_cache' since: 3.2, 'space_cache=v2' since 4.5, default: on) -+ -Options to control the free space cache. This affects performance as searching -for new free blocks could take longer if the space cache is not enabled. On the -other hand, managing the space cache consumes some resources. It can be -disabled without clearing at mount time. -+ -There are two implementations of how the space is tracked. The safe default is -'v1'. On large filesystems (many-terabytes) and certain workloads the 'v1' -performance may degrade. This problem is addressed by 'v2', that is based on -b-trees, sometimes referred to as 'free-space-tree'. -+ -'Compatibility notes:' -+ -* the 'v2' has to be enabled manually at mount time, once -* kernel without 'v2' support will be able to mount the filesystem in read-only mode -* 'v2' can be removed by mounting with 'clear_cache' +('nospace_cache' since: 3.2, 'space_cache=v1' and 'space_cache=v2' since 4.5, default: 'space_cache=v1') ++ +Options to control the free space cache. The free space cache greatly improves +performance when reading block group free space into memory. However, managing +the space cache consumes some resources, including a small amount of disk +space. ++ +There are two implementations of the free space cache. The original +implementation, 'v1', is the safe default. The 'v1' space cache can be disabled +at mount time with 'nospace_cache' without clearing. ++ +On very large filesystems (many terabytes) and certain workloads, the +performance of the 'v1' space cache may degrade drastically. The 'v2' +implementation, which adds a new B-tree called the free space tree, addresses +this issue. Once enabled, the 'v2' space cache will always be used and cannot +be disabled unless it is cleared. Use 'clear_cache,space_cache=v1' or +'clear_cache,nospace_cache' to do so. If 'v2' is enabled, kernels without 'v2' +support will only be able to mount the filesystem in read-only mode. The +`btrfs(8)` command currently only has read-only support for 'v2'. A read-write +command may be run on a 'v2' filesystem by clearing the cache, running the +command, and then remounting with 'space_cache=v2'. ++ +If a version is not explicitly specified, the default implementation will be +chosen, which is 'v1' as of 4.9. *ssd*:: *nossd*:: @@ -434,7 +441,7 @@ dump-super device* will dump a superblock, you can map the value of after mkfs, on a mounted filesystem:: The features of a filesystem (with a given UUID) are listed in `/sys/fs/btrfs/UUID/features/`, one file per feature. The status of is stored -insid the file. The value '1' is for enabled, '0' means the feature was had +insid the file. The value '1' is for enabled, '0' means the feature had been enabled at the mount time and turned off afterwards. + Whether a particular feature can be turned on a mounted filesystem can be found @@ -562,7 +569,7 @@ crw------- 1 root root 10, 234 Jan 1 12:00 /dev/btrfs-control The device accepts some ioctl calls that can perform following actions on the filesyste module: -* scan devices for btrfs filesytem (ie. to let multi-device filesystems mount +* scan devices for btrfs filesystem (ie. to let multi-device filesystems mount automatically) and register them with the kernel module * similar to scan, but also wait until the device scanning process is finished for a given filesystem diff --git a/Documentation/btrfs-qgroup.asciidoc b/Documentation/btrfs-qgroup.asciidoc index 438dbc7d..3053f2e6 100644 --- a/Documentation/btrfs-qgroup.asciidoc +++ b/Documentation/btrfs-qgroup.asciidoc @@ -126,6 +126,10 @@ Prefix \'+' means ascending order and \'-' means descending order of <attr>. If no prefix is given, use ascending order by default. + If multiple <attr>s is given, use comma to separate. ++ +--sync:::: +To retrieve information after updating the state of qgroups, +force sync of the filesystem identified by <path> before getting information. EXIT STATUS ----------- diff --git a/Documentation/btrfs-quota.asciidoc b/Documentation/btrfs-quota.asciidoc index 33c3bfd7..77d4c685 100644 --- a/Documentation/btrfs-quota.asciidoc +++ b/Documentation/btrfs-quota.asciidoc @@ -16,7 +16,7 @@ of a btrfs filesystem. The quota groups (qgroups) are managed by the subcommand `btrfs qgroup`(8). NOTE: the qgroups are different than the traditional user quotas and designed -to track shared and exlusive data per-subvolume. Plese refer to the section +to track shared and exclusive data per-subvolume. Please refer to the section 'HIERARCHICAL QUOTA GROUP CONCEPTS' for a detailed description. PERFORMANCE IMPLICATIONS @@ -91,7 +91,7 @@ Qgroups of level 0 get created automatically when a subvolume/snapshot gets created. The ID of the qgroup corresponds to the ID of the subvolume, so 0/5 is the qgroup for the root subvolume. For the *btrfs qgroup* command, the path to the subvolume can also be used -instead of '0/ID'. For all higher levels, the ID can be choosen freely. +instead of '0/ID'. For all higher levels, the ID can be chosen freely. Each qgroup can contain a set of lower level qgroups, thus creating a hierarchy of qgroups. Figure 1 shows an example qgroup tree. diff --git a/Documentation/btrfs-receive.asciidoc b/Documentation/btrfs-receive.asciidoc index e246603c..a6838e5e 100644 --- a/Documentation/btrfs-receive.asciidoc +++ b/Documentation/btrfs-receive.asciidoc @@ -9,32 +9,37 @@ SYNOPSIS -------- *btrfs receive* [options] <path> +or + +*btrfs receive* --dump [options] + DESCRIPTION ----------- Receive a stream of changes and replicate one or more subvolumes that were -previously used with *btrfs send* The received subvolumes are stored to -'path'. +previously generated by *btrfs send*. The received subvolumes are stored to +'path', unless '--dump' option is given. + +If '--dump' option is specified, *btrfs receive* will only do the validation of +the stream, and print the stream metadata, one operation per line. *btrfs receive* will fail int the following cases: 1. receiving subvolume already exists -2. previously received subvolume was changed after it was received +2. previously received subvolume has been changed after it was received -3. default subvolume has changed or you didn't mount BTRFS filesystem at the toplevel subvolume +3. default subvolume has changed or you didn't mount the filesystem at the toplevel subvolume A subvolume is made read-only after the receiving process finishes succesfully. `Options` -v:: -enable verbose debug output, print each operation (each occurrence of this -option increases the verbosity level) +increase verbosity about performed actions, print details about each operation --f <infile>:: -by default, btrfs receive uses standard input to receive the stream, -use this option to read from a file instead +-f <FILE>:: +read the stream from <FILE> instead of stdin, -C|--chroot:: confine the process to 'path' using `chroot`(1) @@ -42,19 +47,26 @@ confine the process to 'path' using `chroot`(1) -e:: terminate after receiving an 'end cmd' marker in the stream. + -Without this option, the receiver terminates only if an error is encountered -or at end of file +Without this option the receiver side terminates only in case +of an error on end of file. ---max-errors <N>:: -terminate as soon as N errors happened while processing commands from the send -stream, default value is 1, 0 means no limit +-E|--max-errors <NERR>:: +terminate as soon as NERR errors occur while stream processing commands from +the stream ++ +Default value is 1. A value of 0 means no limit. --m <mountpoint>:: +-m <ROOTMOUNT>:: the root mount point of the destination filesystem + By default the mountpoint is searched in '/proc/self/mounts'. -If you do not have '/proc', eg. in a chroot environment, use this option to tell -us where this filesystem is mounted. +If '/proc' is not accessible, eg. in a chroot environment, use this option to +tell us where this filesystem is mounted. + +--dump:: +dump the stream metadata, one line per operation ++ +Does not require the 'path' parameter. The filesystem chanded. EXIT STATUS ----------- diff --git a/Documentation/btrfs-scrub.asciidoc b/Documentation/btrfs-scrub.asciidoc index 40e793c2..f579c39e 100644 --- a/Documentation/btrfs-scrub.asciidoc +++ b/Documentation/btrfs-scrub.asciidoc @@ -20,7 +20,7 @@ structural damage in the filesystem. The user is supposed to run it manually or via a periodic system service. The recommended period is a month but could be less. The estimated device bandwidth -utilization is about 80% on an idle filesytem. The IO priority class is by +utilization is about 80% on an idle filesystem. The IO priority class is by default 'idle' so background scrub should not interfere with normal filesystem operation significantly. @@ -34,7 +34,7 @@ saved position. SUBCOMMAND ---------- *cancel* <path>|<device>:: -If a scrub is running on the filesystem identified by 'path>' cancel it. +If a scrub is running on the filesystem identified by 'path' cancel it. + If a 'device' is specified, the corresponding filesystem is found and *btrfs scrub cancel* behaves as if it was called on that filesystem. @@ -95,7 +95,14 @@ print separate statistics for each device of the filesystem EXIT STATUS ----------- *btrfs scrub* returns a zero exit status if it succeeds. Non zero is -returned in case of failure. +returned in case of failure: + +1:::: +scrub couldn't be performed +2:::: +there is nothing to resume +3:::: +scrub found uncorrectable errors AVAILABILITY ------------ diff --git a/Documentation/btrfs-subvolume.asciidoc b/Documentation/btrfs-subvolume.asciidoc index 0a1ca003..3419b138 100644 --- a/Documentation/btrfs-subvolume.asciidoc +++ b/Documentation/btrfs-subvolume.asciidoc @@ -17,7 +17,7 @@ snapshots. SUBVOLUME AND SNAPSHOT ---------------------- -A subvolume is a part of filesystem with it's own and independent +A subvolume is a part of filesystem with its own and independent file/directory hierarchy. Subvolumes can share file extents. A snapshot is also subvolume, but with a given initial content of the original subvolume. diff --git a/Documentation/btrfstune.asciidoc b/Documentation/btrfstune.asciidoc index 1e9aa703..04295ee3 100644 --- a/Documentation/btrfstune.asciidoc +++ b/Documentation/btrfstune.asciidoc @@ -20,7 +20,7 @@ complete list of features and kernel version of their introduction at https://btrfs.wiki.kernel.org/index.php/Changelog#By_feature . Also, the manual page `mkfs.btrfs`(8) contains more details about the features. -Some of the features could be enabled on a mounted filesytem. Please refer to +Some of the features could be enabled on a mounted filesystem. Please refer to the respective section in `btrfs`(5). OPTIONS diff --git a/Documentation/mkfs.btrfs.asciidoc b/Documentation/mkfs.btrfs.asciidoc index 6515e145..46a4d2d5 100644 --- a/Documentation/mkfs.btrfs.asciidoc +++ b/Documentation/mkfs.btrfs.asciidoc @@ -7,25 +7,7 @@ mkfs.btrfs - create a btrfs filesystem SYNOPSIS -------- -*mkfs.btrfs* -$$[-A|--alloc-start <alloc-start>]$$ -$$[-b|--byte-count <byte-count>]$$ -$$[-d|--data <data-profile>]$$ -$$[-m|--metadata <metadata profile>]$$ -$$[-M|--mixed]$$ -$$[-l|--leafsize <leafsize>]$$ -$$[-n|--nodesize <nodesize>]$$ -$$[-s|--sectorsize <sectorsize>]$$ -$$[-L|--label <label>]$$ -$$[-K|--nodiscard]$$ -$$[-r|--rootdir <rootdir>]$$ -$$[-O|--features <feature1>[,<feature2>...]]$$ -$$[-U|--uuid <UUID>]$$ -$$[-f|--force]$$ -$$[-q|--quiet]$$ -$$[--help]$$ -$$[-V|--version]$$ -$$<device> [<device>...]$$ +*mkfs.btrfs* [options] <device> [<device>...] DESCRIPTION ----------- @@ -118,6 +100,8 @@ bytes and must not contain newline characters. *-K|--nodiscard*:: Do not perform whole device TRIM operation on devices that are capable of that. +This does not affect discard/trim operation when the filesystem is mounted. +Please see the mount option 'discard' for that in `btrfs`(5). *-r|--rootdir <rootdir>*:: Populate the toplevel subvolume with files from 'rootdir'. This does not @@ -182,6 +166,9 @@ root partition created with RAID1/10/5/6 profiles. The mount action can happen before all block devices are discovered. The waiting is usually done on the initramfs/initrd systems. +As of kernel 4.9, RAID5/6 is still considered experimental and shouldn't be +employed for production use. + FILESYSTEM FEATURES ------------------- @@ -271,7 +258,7 @@ There are the following block group types available: | RAID6 | 1 | 2 | 3 to N - 2 | 3/any ^(see note 3)^ |============================================================= -WARNING: It's not recommended to build btrfs with RAID0/1/10/5/6 prfiles on +WARNING: It's not recommended to build btrfs with RAID0/1/10/5/6 profiles on partitions from the same device. Neither redundancy nor performance will be improved. @@ -31,26 +31,27 @@ Building from sources To build from git sources you need to generate the configure script using the autotools: - $ ./autogen.sh + $ ./autogen.sh To build from the released tarballs: - $ ./configure - $ make - $ make install + $ ./configure + $ make + $ make install You may disable building some parts like documentation, btrfs-convert or backtrace support. See ./configure --help for more. Specific CFLAGS or LDFLAGS should be set like - $ CFLAGS=... LDFLAGS=... ./configure --prefix=/usr + $ CFLAGS=... LDFLAGS=... ./configure --prefix=/usr and not as arguments to make. You can specify additional flags to build via variables EXTRA_CFLAGS and EXTRA_LDFLAGS that get appended to the predefined -values of the respective variables. +values of the respective variables. There are further build tuning options +documented in the Makefile. - $ make EXTRA_CFLAGS=-ggdb3 + $ make EXTRA_CFLAGS=-ggdb3 The build utilizes autotools, dependencies for generating the configure scripts are: @@ -60,18 +61,18 @@ scripts are: * pkg-config -Staticly built binaries ------------------------ +Statically built binaries +------------------------- The makefiles are ready to let you build static binaries of the utilities. This may be handy in rescue environments. Your system has to provide static version of the libraries. -$ make static -$ make btrfs.static -$ make btrfs-convert.static + $ make static + $ make btrfs.static + $ make btrfs-convert.static -The resulting static binaries have the '.static' suffix, the intermediate object +The resulting binaries have the '.static' suffix, the intermediate object files do not conflict with the normal (dynamic) build. diff --git a/Makefile.in b/Makefile.in index 214b0428..0e3a0a0f 100644 --- a/Makefile.in +++ b/Makefile.in @@ -19,11 +19,17 @@ # all - shortcut for all of the above # asan - enable address sanitizer compiler feature # ubsan - undefined behaviour sanitizer compiler feature +# bcheck - extended build checks # W=123 build with warnings (default: off) # DEBUG_CFLAGS additional compiler flags for debugging build # EXTRA_CFLAGS additional compiler flags # EXTRA_LDFLAGS additional linker flags # +# Testing-specific options (see also tests/README.md): +# TEST=GLOB run test(s) from directories matching GLOB +# TEST_LOG=tty print name of a command run via the execution helpers +# TEST_LOG=dump dump testing log file when a test fails +# # Static checkers: # CHECKER static checker binary to be called (default: sparse) # CHECKER_FLAGS flags to pass to CHECKER, can override CFLAGS @@ -51,6 +57,8 @@ DEBUG_CFLAGS_DEFAULT = -O0 -U_FORTIFY_SOURCE -ggdb3 DEBUG_CFLAGS_INTERNAL = DEBUG_CFLAGS := +TOPDIR := $(shell pwd) + # Common build flags CFLAGS = @CFLAGS@ \ -include config.h \ @@ -58,12 +66,14 @@ CFLAGS = @CFLAGS@ \ -D_XOPEN_SOURCE=700 \ -fno-strict-aliasing \ -fPIC \ + -I$(TOPDIR) \ + -I$(TOPDIR)/kernel-lib \ $(EXTRAWARN_CFLAGS) \ $(DEBUG_CFLAGS_INTERNAL) \ $(EXTRA_CFLAGS) LDFLAGS = @LDFLAGS@ \ - -rdynamic $(EXTRA_LDFLAGS) + -rdynamic -L$(TOPDIR) $(EXTRA_LDFLAGS) LIBS = @UUID_LIBS@ @BLKID_LIBS@ @ZLIB_LIBS@ @LZO2_LIBS@ -L. -pthread LIBBTRFS_LIBS = $(LIBS) @@ -83,22 +93,24 @@ CHECKER_FLAGS := -include $(check_defs) -D__CHECKER__ \ -D__CHECK_ENDIAN__ -Wbitwise -Wuninitialized -Wshadow -Wundef \ -U_FORTIFY_SOURCE -objects = ctree.o disk-io.o radix-tree.o extent-tree.o print-tree.o \ +objects = ctree.o disk-io.o kernel-lib/radix-tree.o extent-tree.o print-tree.o \ root-tree.o dir-item.o file-item.o inode-item.o inode-map.o \ extent-cache.o extent_io.o volumes.o utils.o repair.o \ - qgroup.o raid6.o free-space-cache.o list_sort.o props.o \ + qgroup.o raid56.o free-space-cache.o kernel-lib/list_sort.o props.o \ ulist.o qgroup-verify.o backref.o string-table.o task-utils.o \ - inode.o file.o find-root.o free-space-tree.o help.o + inode.o file.o find-root.o free-space-tree.o help.o send-dump.o cmds_objects = cmds-subvolume.o cmds-filesystem.o cmds-device.o cmds-scrub.o \ cmds-inspect.o cmds-balance.o cmds-send.o cmds-receive.o \ cmds-quota.o cmds-qgroup.o cmds-replace.o cmds-check.o \ cmds-restore.o cmds-rescue.o chunk-recover.o super-recover.o \ cmds-property.o cmds-fi-usage.o cmds-inspect-dump-tree.o \ cmds-inspect-dump-super.o cmds-inspect-tree-stats.o cmds-fi-du.o -libbtrfs_objects = send-stream.o send-utils.o rbtree.o btrfs-list.o crc32c.o \ +libbtrfs_objects = send-stream.o send-utils.o kernel-lib/rbtree.o btrfs-list.o \ + kernel-lib/crc32c.o \ uuid-tree.o utils-lib.o rbtree-utils.o -libbtrfs_headers = send-stream.h send-utils.h send.h rbtree.h btrfs-list.h \ - crc32c.h list.h kerncompat.h radix-tree.h extent-cache.h \ +libbtrfs_headers = send-stream.h send-utils.h send.h kernel-lib/rbtree.h btrfs-list.h \ + kernel-lib/crc32c.h kernel-lib/list.h kerncompat.h \ + kernel-lib/radix-tree.h extent-cache.h \ extent_io.h ioctl.h ctree.h btrfsck.h version.h TESTS = fsck-tests.sh convert-tests.sh @@ -155,18 +167,23 @@ ifneq (,$(findstring ubsan,$(D))) DEBUG_CFLAGS_INTERNAL += -fsanitize=undefined endif +ifneq (,$(findstring bcheck,$(D))) + DEBUG_CFLAGS_INTERNAL += -DDEBUG_BUILD_CHECKS +endif + MAKEOPTS = --no-print-directory Q=$(Q) # build all by default -progs = $(progs_install) btrfsck btrfs-corrupt-block btrfs-calc-size +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 \ - btrfs-find-root btrfstune btrfs-show-super \ + btrfs-find-root btrfstune \ btrfs-select-super -progs_extra = btrfs-fragments +# other tools, not built by default +progs_extra = btrfs-fragments btrfs-calc-size btrfs-show-super progs_static = $(foreach p,$(progs),$(p).static) @@ -231,7 +248,7 @@ else endif %.o.d: %.c - $(Q)$(CC) -MM -MG -MF $@ -MT $(@:.o.d=.o) -MT $(@:.o.d=.static.o) -MT $@ $(CFLAGS) $< + $(Q)$(CC) -MD -MM -MG -MF $@ -MT $(@:.o.d=.o) -MT $(@:.o.d=.static.o) -MT $@ $(CFLAGS) $< # # Pick from per-file variables, btrfs_*_cflags @@ -240,11 +257,13 @@ endif @$(check_echo) " [SP] $<" $(Q)$(check) $(CFLAGS) $(CHECKER_FLAGS) $< @echo " [CC] $@" - $(Q)$(CC) $(CFLAGS) -c $< $($(subst -,_,$(@:%.o=%)-cflags)) + $(Q)$(CC) $(CFLAGS) -c $< -o $@ $($(subst -,_,$(@:%.o=%)-cflags)) \ + $($(subst -,_,btrfs-$(@:%/$(notdir $@)=%)-cflags)) %.static.o: %.c @echo " [CC] $@" - $(Q)$(CC) $(STATIC_CFLAGS) -c $< -o $@ $($(subst -,_,$(@:%.static.o=%)-cflags)) + $(Q)$(CC) $(STATIC_CFLAGS) -c $< -o $@ $($(subst -,_,$(@:%.static.o=%)-cflags)) \ + $($(subst -,_,btrfs-$(@:%/$(notdir $@)=%)-cflags)) all: $(progs) $(BUILDDIRS) $(SUBDIRS): $(BUILDDIRS) @@ -256,6 +275,7 @@ test-convert: btrfs btrfs-convert @echo " [TEST] convert-tests.sh" $(Q)bash tests/convert-tests.sh +test-check: test-fsck test-fsck: btrfs btrfs-image btrfs-corrupt-block btrfs-debug-tree mkfs.btrfs @echo " [TEST] fsck-tests.sh" $(Q)bash tests/fsck-tests.sh @@ -286,7 +306,7 @@ test-inst: all $(MAKE) DESTDIR=$$tmpdest install && \ $(RM) -rf -- $$tmpdest -test: test-fsck test-mkfs test-convert test-misc test-fuzz +test: test-fsck test-mkfs test-convert test-misc test-fuzz test-cli # # NOTE: For static compiles, you need to have all the required libs @@ -354,13 +374,13 @@ btrfsck.static: btrfs.static @echo " [LN] $@" $(Q)$(LN_S) -f $^ $@ -mkfs.btrfs: $(objects) $(libs_static) mkfs.o +mkfs.btrfs: $(objects) $(libs_static) mkfs/main.o @echo " [LD] $@" - $(Q)$(CC) $(CFLAGS) -o mkfs.btrfs $(objects) $(libs_static) mkfs.o $(LDFLAGS) $(LIBS) + $(Q)$(CC) $(CFLAGS) -o mkfs.btrfs $(objects) $(libs_static) mkfs/main.o $(LDFLAGS) $(LIBS) -mkfs.btrfs.static: $(static_objects) mkfs.static.o $(static_libbtrfs_objects) +mkfs.btrfs.static: $(static_objects) mkfs/main.static.o $(static_libbtrfs_objects) @echo " [LD] $@" - $(Q)$(CC) $(STATIC_CFLAGS) -o mkfs.btrfs.static mkfs.static.o $(static_objects) \ + $(Q)$(CC) $(STATIC_CFLAGS) -o mkfs.btrfs.static mkfs/main.static.o $(static_objects) \ $(static_libbtrfs_objects) $(STATIC_LDFLAGS) $(STATIC_LIBS) btrfstune: $(objects) $(libs_static) btrfstune.o @@ -372,6 +392,25 @@ btrfstune.static: $(static_objects) btrfstune.static.o $(static_libbtrfs_objects $(Q)$(CC) $(STATIC_CFLAGS) -o $@ btrfstune.static.o $(static_objects) \ $(static_libbtrfs_objects) $(STATIC_LDFLAGS) $(STATIC_LIBS) +btrfs-image: $(objects) $(libs_static) image/main.o + @echo " [LD] $@" + $(Q)$(CC) $(CFLAGS) -I$(TOPDIR)/image -o btrfs-image $(objects) image/main.o $(libs_static) $(LDFLAGS) $(LIBS) + +btrfs-image.static: $(static_objects) image/main.static.o $(static_libbtrfs_objects) + @echo " [LD] $@" + $(Q)$(CC) $(STATIC_CFLAGS) -o $@ image/main.static.o $(static_objects) \ + $(static_libbtrfs_objects) $(STATIC_LDFLAGS) $(STATIC_LIBS) + +btrfs-convert: $(objects) $(libs_static) convert/main.o + @echo " [LD] $@" + $(Q)$(CC) $(CFLAGS) -I$(TOPDIR)/convert -o btrfs-convert $(objects) convert/main.o $(libs_static) \ + $(LDFLAGS) $(btrfs_convert_libs) $(LIBS) + +btrfs-convert.static: $(static_objects) convert/main.static.o $(static_libbtrfs_objects) + @echo " [LD] $@" + $(Q)$(CC) $(STATIC_CFLAGS) -o $@ convert/main.static.o $(static_objects) \ + $(static_libbtrfs_objects) $(STATIC_LDFLAGS) $(btrfs_convert_libs) $(STATIC_LIBS) + dir-test: $(objects) $(libs) dir-test.o @echo " [LD] $@" $(Q)$(CC) $(CFLAGS) -o dir-test $(objects) $(libs) dir-test.o $(LDFLAGS) $(LIBS) @@ -380,21 +419,53 @@ quick-test: $(objects) $(libs) quick-test.o @echo " [LD] $@" $(Q)$(CC) $(CFLAGS) -o quick-test $(objects) $(libs) quick-test.o $(LDFLAGS) $(LIBS) -ioctl-test: $(objects) $(libs) ioctl-test.o - @echo " [LD] $@" - $(Q)$(CC) $(CFLAGS) -o ioctl-test $(objects) $(libs) ioctl-test.o $(LDFLAGS) $(LIBS) - -send-test: $(objects) $(libs) send-test.o - @echo " [LD] $@" - $(Q)$(CC) $(CFLAGS) -o send-test $(objects) $(libs) send-test.o $(LDFLAGS) $(LIBS) +ioctl-test.o: ioctl-test.c ioctl.h kerncompat.h ctree.h + @echo " [CC] $@" + $(Q)$(CC) $(CFLAGS) -c $< -o $@ + +ioctl-test-32.o: ioctl-test.c ioctl.h kerncompat.h ctree.h + @echo " [CC32] $@" + $(Q)$(CC) $(CFLAGS) -m32 -c $< -o $@ + +ioctl-test-64.o: ioctl-test.c ioctl.h kerncompat.h ctree.h + @echo " [CC64] $@" + $(Q)$(CC) $(CFLAGS) -m64 -c $< -o $@ + +ioctl-test: ioctl-test.o + @echo " [LD] $@" + $(Q)$(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) + @echo " ?[PAHOLE] $@.pahole" + -$(Q)pahole $@ > $@.pahole + +ioctl-test-32: ioctl-test-32.o + @echo " [LD32] $@" + $(Q)$(CC) $(CFLAGS) -m32 -o $@ $< $(LDFLAGS) + @echo " ?[PAHOLE] $@.pahole" + -$(Q)pahole $@ > $@.pahole + +ioctl-test-64: ioctl-test-64.o + @echo " [LD64] $@" + $(Q)$(CC) $(CFLAGS) -m64 -o $@ $< $(LDFLAGS) + @echo " ?[PAHOLE] $@.pahole" + -$(Q)pahole $@ > $@.pahole + +test-ioctl: ioctl-test ioctl-test-32 ioctl-test-64 + @echo " [TEST/ioctl]" + $(Q)./ioctl-test > ioctl-test.log + $(Q)./ioctl-test-32 > ioctl-test-32.log + $(Q)./ioctl-test-64 > ioctl-test-64.log library-test: $(libs_shared) library-test.o @echo " [LD] $@" - $(Q)$(CC) $(CFLAGS) -o library-test library-test.o $(LDFLAGS) -lbtrfs + $(Q)$(CC) $(CFLAGS) -o library-test library-test.o $(LDFLAGS) -Wl,-rpath=$(TOPDIR) -lbtrfs + @echo " [TEST] $@" + $(Q)./$@ -library-test.static: $(libs_static) library-test.o +library-test.static: $(libs_static) library-test.static.o @echo " [LD] $@" - $(Q)$(CC) $(CFLAGS) -o library-test-static library-test.o $(LDFLAGS) $(libs_static) + $(Q)$(CC) $(STATIC_CFLAGS) -o library-test.static library-test.static.o $(STATIC_LDFLAGS) $(libs_static) $(STATIC_LIBS) + @echo " [TEST] $@" + $(Q)./$@ test-build: test-build-pre test-build-real @@ -418,8 +489,12 @@ clean-all: clean clean-doc clean-gen clean: $(CLEANDIRS) @echo "Cleaning" - $(Q)$(RM) -f $(progs) cscope.out *.o *.o.d \ - dir-test ioctl-test quick-test send-test library-test library-test-static \ + $(Q)$(RM) -f -- $(progs) cscope.out *.o *.o.d \ + kernel-lib/*.o kernel-lib/*.o.d \ + image/*.o image/*.o.d \ + convert/*.o convert/*.o.d \ + mkfs/*.o mkfs/*.o.d \ + dir-test ioctl-test quick-test library-test library-test-static \ btrfs.static mkfs.btrfs.static \ $(check_defs) \ $(libs) $(lib_links) \ @@ -431,7 +506,7 @@ clean-doc: clean-gen: @echo "Cleaning Generated Files" - $(Q)$(RM) -rf version.h config.status config.cache connfig.log \ + $(Q)$(RM) -rf -- version.h config.status config.cache connfig.log \ configure.lineno config.status.lineno Makefile \ Documentation/Makefile \ config.log config.h config.h.in~ aclocal.m4 \ @@ -469,10 +544,10 @@ $(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)$(libdir); $(RM) -f $(lib_links) $(libs) - cd $(DESTDIR)$(bindir); $(RM) -f btrfsck fsck.btrfs $(progs_install) + cd $(DESTDIR)$(incdir); $(RM) -f -- $(headers) + $(RMDIR) -p --ignore-fail-on-non-empty -- $(DESTDIR)$(incdir) + cd $(DESTDIR)$(libdir); $(RM) -f -- $(lib_links) $(libs) + cd $(DESTDIR)$(bindir); $(RM) -f -- btrfsck fsck.btrfs $(progs_install) ifneq ($(MAKECMDGOALS),clean) -include $(objects:.o=.o.d) $(cmds_objects:.o=.o.d) $(subst .btrfs,, $(filter-out btrfsck.o.d, $(progs:=.o.d))) @@ -1,4 +1,4 @@ -Btrfs-progs +Btrfs-progs [](https://travis-ci.org/kdave/btrfs-progs) [](https://scan.coverity.com/projects/btrfs-progs) =========== Userspace utilities to manage btrfs filesystems. @@ -14,28 +14,79 @@ This repository hosts following utilities: * **btrfs** — the main administration tool ([manual page](https://btrfs.wiki.kernel.org/index.php/Manpage/btrfs)) * **mkfs.btrfs** — utility to create the filesystem ([manual page](https://btrfs.wiki.kernel.org/index.php/Manpage/mkfs.btrfs)) -See INSTALL for build instructions. +See INSTALL for build instructions and [tests/README.md](tests/README.md) for +testing information. Release cycle ------------- The major version releases are time-based and follow the cycle of the linux kernel releases. The cycle usually takes 2 months. A minor version releases may -happen in the meantime if there are queued bug fixes or minor useful -improvements. +happen in the meantime if there are bug fixes or minor useful improvements +queued. + +The release tags are signed with a GPG key ID `F2B4 1200 C54E FB30 380C 1756 C565 D5F9 D76D 583B`, +release tarballs are hosted at [kernel.org](https://www.kernel.org/pub/linux/kernel/people/kdave/btrfs-progs/). +See file [CHANGES](CHANGES) or [changelogs on wiki](https://btrfs.wiki.kernel.org/index.php/Changelog#By_version_.28btrfs-progs.29). + +Reporting bugs +-------------- + +There are several ways, each has its own specifics and audience that can give +feedback or work on a fix. + +* [bugzilla.kernel.org](https://bugzilla.kernel.org) -- (requires + registration), set the product to Filesystems and component Btrfs, please put + 'btrfs-progs' into the subject so it's clear that it's not a kernel bug + report +* to the mailing list *linux-btrfs@vger.kernel.org* -- (not required to + subscribe), beware that the mail might get overlooked in other traffic +* [github issue tracker](https://github.com/kdave/btrfs-sprogs/issues) +* IRC (irc.freenode.net #btrfs) -- good for discussions eg. if a bug is already + known, but reports could miss developers' attention + Development ----------- The patch submissions, development or general discussions take place at -*linux-btrfs@vger.kernel.org* mailinglist, subsciption not required. +*linux-btrfs@vger.kernel.org* mailinglist, subsciption is not required to post. + +The GitHub pull requests will not be accepted directly, the preferred way is to +send patches to the mailinglist instead. You can link to a branch in any git +repository if the mails do not make it to the mailinglist or just for +convenience (makes it easier to test). + +The development model of btrfs-progs shares a lot with the kernel model. The +github way is different in some ways. We, the upstream community, expect that +the patches meet some criteria (often lacking in github contributions): + +* **one logical change per patch**: eg. not mixing bugfixes, cleanups, features + etc., sometimes it's not clear and will be usually pointed out during reviews +* proper **subject line**: eg. prefix with _btrfs-progs: subpart, ..._ , + descriptive yet not too long, see `git log --oneline` for some inspiration +* proper **changelog**: the changelogs are often missing or lacking explanation _why_ + the change was made, or _how_ is something broken, _what_ are user-visible + effects of the bug or the fix, _how_ does an improvement help or the intended + _usecase_ +* the **Signed-off-by** line: this documents who authored the change, you can read + more about the _The Developer's Certificate of Origin_ [here (chapter 11)](https://www.kernel.org/doc/Documentation/SubmittingPatches) + +Documentation updates +--------------------- + +Documentation fixes or updates do not need much explanation so sticking to the +code rules in the previous section is not necessary. Github pull requests are +OK, patches could be sent to me directly and not required to be also in the +mailinglist. Pointing out typos via IRC also works, although might get +accidentally lost in the noise. References ---------- -* [Wiki with more information](https://btrfs.wiki.kernel.org) -* [Btrfs-progs changelogs](https://btrfs.wiki.kernel.org/index.php/Changelog#By_version_.28btrfs-progs.29) -* [wiki/FAQ](https://btrfs.wiki.kernel.org/index.php/FAQ) +* [wiki/Developer's FAQ](https://btrfs.wiki.kernel.org/index.php/Developer's_FAQ) * [wiki/Getting started](https://btrfs.wiki.kernel.org/index.php/Getting_started) * [wiki/TODO](https://btrfs.wiki.kernel.org/index.php/Project_ideas#Userspace_tools_projects) -* [wiki/Developer's FAQ](https://btrfs.wiki.kernel.org/index.php/Developer's_FAQ) +* [Btrfs-progs changelogs](https://btrfs.wiki.kernel.org/index.php/Changelog#By_version_.28btrfs-progs.29) +* [wiki/FAQ](https://btrfs.wiki.kernel.org/index.php/FAQ) +* [Wiki with more information](https://btrfs.wiki.kernel.org) @@ -739,8 +739,7 @@ static int find_parent_nodes(struct btrfs_trans_handle *trans, key.objectid = bytenr; key.offset = (u64)-1; - if (btrfs_fs_incompat(fs_info, - BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA)) + if (btrfs_fs_incompat(fs_info, SKINNY_METADATA)) key.type = BTRFS_METADATA_ITEM_KEY; else key.type = BTRFS_EXTENT_ITEM_KEY; @@ -990,7 +989,7 @@ int btrfs_find_one_extref(struct btrfs_root *root, u64 inode_objectid, unsigned long ptr; key.objectid = inode_objectid; - btrfs_set_key_type(&key, BTRFS_INODE_EXTREF_KEY); + key.type = BTRFS_INODE_EXTREF_KEY; key.offset = start_off; ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); @@ -1030,7 +1029,7 @@ int btrfs_find_one_extref(struct btrfs_root *root, u64 inode_objectid, ret = -ENOENT; if (found_key.objectid != inode_objectid) break; - if (btrfs_key_type(&found_key) != BTRFS_INODE_EXTREF_KEY) + if (found_key.type != BTRFS_INODE_EXTREF_KEY) break; ret = 0; @@ -1136,8 +1135,7 @@ int extent_from_logical(struct btrfs_fs_info *fs_info, u64 logical, struct btrfs_extent_item *ei; struct btrfs_key key; - if (btrfs_fs_incompat(fs_info, - BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA)) + if (btrfs_fs_incompat(fs_info, SKINNY_METADATA)) key.type = BTRFS_METADATA_ITEM_KEY; else key.type = BTRFS_EXTENT_ITEM_KEY; diff --git a/btrfs-calc-size.c b/btrfs-calc-size.c index 835fcc50..1c5c61df 100644 --- a/btrfs-calc-size.c +++ b/btrfs-calc-size.c @@ -25,6 +25,9 @@ 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); diff --git a/btrfs-corrupt-block.c b/btrfs-corrupt-block.c index 789cbc70..0e1eb524 100644 --- a/btrfs-corrupt-block.c +++ b/btrfs-corrupt-block.c @@ -65,7 +65,7 @@ static int debug_corrupt_block(struct extent_buffer *eb, "mirror %d logical %llu physical %llu device %s\n", mirror_num, (unsigned long long)bytenr, (unsigned long long)eb->dev_bytenr, device->name); - kfree(multi); + free(multi); if (!copy || mirror_num == copy) { ret = read_extent_from_disk(eb, 0, eb->len); @@ -308,6 +308,13 @@ static void btrfs_corrupt_extent_tree(struct btrfs_trans_handle *trans, enum btrfs_inode_field { BTRFS_INODE_FIELD_ISIZE, BTRFS_INODE_FIELD_NBYTES, + BTRFS_INODE_FIELD_NLINK, + BTRFS_INODE_FIELD_GENERATION, + BTRFS_INODE_FIELD_TRANSID, + BTRFS_INODE_FIELD_BLOCK_GROUP, + BTRFS_INODE_FIELD_MODE, + BTRFS_INODE_FIELD_UID, + BTRFS_INODE_FIELD_GID, BTRFS_INODE_FIELD_BAD, }; @@ -346,6 +353,20 @@ static enum btrfs_inode_field convert_inode_field(char *field) return BTRFS_INODE_FIELD_ISIZE; if (!strncmp(field, "nbytes", FIELD_BUF_LEN)) return BTRFS_INODE_FIELD_NBYTES; + if (!strncmp(field, "nlink", FIELD_BUF_LEN)) + return BTRFS_INODE_FIELD_NLINK; + if (!strncmp(field, "generation", FIELD_BUF_LEN)) + return BTRFS_INODE_FIELD_GENERATION; + if (!strncmp(field, "transid", FIELD_BUF_LEN)) + return BTRFS_INODE_FIELD_TRANSID; + if (!strncmp(field, "block_group", FIELD_BUF_LEN)) + return BTRFS_INODE_FIELD_BLOCK_GROUP; + if (!strncmp(field, "mode", FIELD_BUF_LEN)) + return BTRFS_INODE_FIELD_MODE; + if (!strncmp(field, "uid", FIELD_BUF_LEN)) + return BTRFS_INODE_FIELD_UID; + if (!strncmp(field, "gid", FIELD_BUF_LEN)) + return BTRFS_INODE_FIELD_GID; return BTRFS_INODE_FIELD_BAD; } @@ -603,6 +624,41 @@ static int corrupt_inode(struct btrfs_trans_handle *trans, bogus = generate_u64(orig); btrfs_set_inode_nbytes(path->nodes[0], ei, bogus); break; + case BTRFS_INODE_FIELD_NLINK: + orig = btrfs_inode_nlink(path->nodes[0], ei); + bogus = generate_u32(orig); + btrfs_set_inode_nlink(path->nodes[0], ei, bogus); + break; + case BTRFS_INODE_FIELD_GENERATION: + orig = btrfs_inode_generation(path->nodes[0], ei); + bogus = generate_u64(orig); + btrfs_set_inode_generation(path->nodes[0], ei, bogus); + break; + case BTRFS_INODE_FIELD_TRANSID: + orig = btrfs_inode_transid(path->nodes[0], ei); + bogus = generate_u64(orig); + btrfs_set_inode_transid(path->nodes[0], ei, bogus); + break; + case BTRFS_INODE_FIELD_BLOCK_GROUP: + orig = btrfs_inode_block_group(path->nodes[0], ei); + bogus = generate_u64(orig); + btrfs_set_inode_block_group(path->nodes[0], ei, bogus); + break; + case BTRFS_INODE_FIELD_MODE: + orig = btrfs_inode_mode(path->nodes[0], ei); + bogus = generate_u32(orig); + btrfs_set_inode_mode(path->nodes[0], ei, bogus); + break; + case BTRFS_INODE_FIELD_UID: + orig = btrfs_inode_uid(path->nodes[0], ei); + bogus = generate_u32(orig); + btrfs_set_inode_uid(path->nodes[0], ei, bogus); + break; + case BTRFS_INODE_FIELD_GID: + orig = btrfs_inode_gid(path->nodes[0], ei); + bogus = generate_u32(orig); + btrfs_set_inode_gid(path->nodes[0], ei, bogus); + break; default: ret = -EINVAL; break; @@ -899,7 +955,11 @@ static int corrupt_item_nocow(struct btrfs_trans_handle *trans, if (slot == 0) del = 0; /* Only accept valid eb */ - BUG_ON(!leaf->data || slot >= btrfs_header_nritems(leaf)); + if (slot >= btrfs_header_nritems(leaf)) { + error("invalid eb: no data or slot out of range: %d >= %d", + slot, btrfs_header_nritems(leaf)); + return -EINVAL; + } btrfs_item_key_to_cpu(leaf, &key, slot); if (del) { fprintf(stdout, "Deleting key and data [%llu, %u, %llu].\n", diff --git a/btrfs-debugfs b/btrfs-debugfs index 0a654a64..dfb88539 100755 --- a/btrfs-debugfs +++ b/btrfs-debugfs @@ -4,7 +4,7 @@ # LGPLv2 license # Copyright Facebook 2014 -import sys,os,struct,fcntl,ctypes,stat,argparse +import sys, os, fcntl, ctypes, stat, argparse # helpers for max ints maxu64 = (1L << 64) - 1 @@ -233,7 +233,6 @@ def print_file_extents(filename): s.args.min_objectid = st.st_ino s.args.max_objectid = st.st_ino - size = st.st_size while True: try: @@ -314,7 +313,7 @@ def print_block_groups(mountpoint): try: fd = os.open(mountpoint, os.O_RDONLY) - st = os.fstat(fd) + os.fstat(fd) except Exception, e: sys.stderr.write("Failed to open %s (%s)\n" % (mountpoint, e)) return -1 @@ -336,7 +335,7 @@ def print_block_groups(mountpoint): h = ctypes.addressof(header) p_left = args_buffer_size - for x in xrange(0, s.args.nr_items): + for _ in xrange(0, s.args.nr_items): # for each itme, copy the header from the buffer into # our header struct ctypes.memmove(h, p, header_size) diff --git a/btrfs-fragments.c b/btrfs-fragments.c index eb75eb7a..46c78d2b 100644 --- a/btrfs-fragments.c +++ b/btrfs-fragments.c @@ -218,7 +218,7 @@ list_fragments(int fd, u64 flags, char *dir) memset(&args, 0, sizeof(args)); - sk->tree_id = 2; + sk->tree_id = BTRFS_EXTENT_TREE_OBJECTID; sk->max_type = -1; sk->min_type = 0; sk->max_objectid = (u64)-1; diff --git a/btrfs-list.c b/btrfs-list.c index 4cc2ed49..8eec05ea 100644 --- a/btrfs-list.c +++ b/btrfs-list.c @@ -113,7 +113,7 @@ void btrfs_list_setup_print_column(enum btrfs_list_column_enum column) { int i; - BUG_ON(column < 0 || column > BTRFS_LIST_ALL); + ASSERT(0 <= column && column <= BTRFS_LIST_ALL); if (column < BTRFS_LIST_ALL) { btrfs_list_columns[column].need_print = 1; @@ -124,11 +124,6 @@ void btrfs_list_setup_print_column(enum btrfs_list_column_enum column) btrfs_list_columns[i].need_print = 1; } -static void root_lookup_init(struct root_lookup *tree) -{ - tree->root.rb_node = NULL; -} - static int comp_entry_with_rootid(struct root_info *entry1, struct root_info *entry2, int is_descending) @@ -237,20 +232,15 @@ struct btrfs_list_comparer_set *btrfs_list_alloc_comparer_set(void) return set; } -void btrfs_list_free_comparer_set(struct btrfs_list_comparer_set *comp_set) -{ - free(comp_set); -} - static int btrfs_list_setup_comparer(struct btrfs_list_comparer_set **comp_set, enum btrfs_list_comp_enum comparer, int is_descending) { struct btrfs_list_comparer_set *set = *comp_set; int size; - BUG_ON(!set); - BUG_ON(comparer >= BTRFS_LIST_COMP_MAX); - BUG_ON(set->ncomps > set->total); + ASSERT(set != NULL); + ASSERT(comparer < BTRFS_LIST_COMP_MAX); + ASSERT(set->ncomps <= set->total); if (set->ncomps == set->total) { void *tmp; @@ -272,7 +262,7 @@ static int btrfs_list_setup_comparer(struct btrfs_list_comparer_set **comp_set, *comp_set = set; } - BUG_ON(set->comps[set->ncomps].comp_func); + ASSERT(set->comps[set->ncomps].comp_func == NULL); set->comps[set->ncomps].comp_func = all_comp_funcs[comparer]; set->comps[set->ncomps].is_descending = is_descending; @@ -287,7 +277,7 @@ static int sort_comp(struct root_info *entry1, struct root_info *entry2, int i, ret = 0; if (!set || !set->ncomps) - goto comp_rootid; + return comp_entry_with_rootid(entry1, entry2, 0); for (i = 0; i < set->ncomps; i++) { if (!set->comps[i].comp_func) @@ -302,10 +292,8 @@ static int sort_comp(struct root_info *entry1, struct root_info *entry2, rootid_compared = 1; } - if (!rootid_compared) { -comp_rootid: + if (!rootid_compared) ret = comp_entry_with_rootid(entry1, entry2, 0); - } return ret; } @@ -399,7 +387,7 @@ static struct root_info *root_tree_search(struct root_lookup *root_tree, static int update_root(struct root_lookup *root_lookup, u64 root_id, u64 ref_tree, u64 root_offset, u64 flags, u64 dir_id, char *name, int name_len, u64 ogen, u64 gen, - time_t ot, void *uuid, void *puuid, void *ruuid) + time_t otime, u8 *uuid, u8 *puuid, u8 *ruuid) { struct root_info *ri; @@ -431,8 +419,8 @@ static int update_root(struct root_lookup *root_lookup, ri->ogen = ogen; if (!ri->ogen && root_offset) ri->ogen = root_offset; - if (ot) - ri->otime = ot; + if (otime) + ri->otime = otime; if (uuid) memcpy(&ri->uuid, uuid, BTRFS_UUID_SIZE); if (puuid) @@ -454,7 +442,7 @@ static int update_root(struct root_lookup *root_lookup, * name_len: the length of name * ogen: the original generation of the root * gen: the current generation of the root - * ot: the original time(create time) of the root + * otime: the original time (creation time) of the root * uuid: uuid of the root * puuid: uuid of the root parent if any * ruuid: uuid of the received subvol, if any @@ -462,13 +450,13 @@ static int update_root(struct root_lookup *root_lookup, static int add_root(struct root_lookup *root_lookup, u64 root_id, u64 ref_tree, u64 root_offset, u64 flags, u64 dir_id, char *name, int name_len, u64 ogen, u64 gen, - time_t ot, void *uuid, void *puuid, void *ruuid) + time_t otime, u8 *uuid, u8 *puuid, u8 *ruuid) { struct root_info *ri; int ret; ret = update_root(root_lookup, root_id, ref_tree, root_offset, flags, - dir_id, name, name_len, ogen, gen, ot, + dir_id, name, name_len, ogen, gen, otime, uuid, puuid, ruuid); if (!ret) return 0; @@ -503,8 +491,8 @@ static int add_root(struct root_lookup *root_lookup, ri->ogen = ogen; if (!ri->ogen && root_offset) ri->ogen = root_offset; - if (ot) - ri->otime = ot; + if (otime) + ri->otime = otime; if (uuid) memcpy(&ri->uuid, uuid, BTRFS_UUID_SIZE); @@ -516,14 +504,27 @@ static int add_root(struct root_lookup *root_lookup, memcpy(&ri->ruuid, ruuid, BTRFS_UUID_SIZE); ret = root_tree_insert(root_lookup, ri); - if (ret) { - printf("failed to insert tree %llu\n", (unsigned long long)root_id); + if (ret < 0) { + error("failed to insert subvolume %llu to tree: %s", + (unsigned long long)root_id, strerror(-ret)); exit(1); } return 0; } -static void __free_root_info(struct rb_node *node) +/* + * Simplified add_root for back references, omits the uuid and original info + * parameters, root offset and flags. + */ +static int add_root_backref(struct root_lookup *root_lookup, u64 root_id, + u64 ref_tree, u64 dir_id, char *name, int name_len) +{ + return add_root(root_lookup, root_id, ref_tree, 0, 0, dir_id, name, + name_len, 0, 0, 0, NULL, NULL, NULL); +} + + +static void free_root_info(struct rb_node *node) { struct root_info *ri; @@ -534,11 +535,6 @@ static void __free_root_info(struct rb_node *node) free(ri); } -static inline void __free_all_subvolumn(struct root_lookup *root_tree) -{ - rb_free_nodes(&root_tree->root, __free_root_info); -} - /* * for a given root_info, search through the root_lookup tree to construct * the full path name to it. @@ -648,9 +644,8 @@ static int lookup_ino_path(int fd, struct root_info *ri) ri->ref_tree = 0; return -ENOENT; } - fprintf(stderr, "ERROR: Failed to lookup path for root %llu - %s\n", - (unsigned long long)ri->ref_tree, - strerror(errno)); + error("failed to lookup path for root %llu: %s", + (unsigned long long)ri->ref_tree, strerror(errno)); return ret; } @@ -700,7 +695,7 @@ static u64 find_root_gen(int fd) /* this ioctl fills in ino_args->treeid */ ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &ino_args); if (ret < 0) { - fprintf(stderr, "ERROR: Failed to lookup path for dirid %llu - %s\n", + error("failed to lookup path for dirid %llu: %s", (unsigned long long)BTRFS_FIRST_FREE_OBJECTID, strerror(errno)); return 0; @@ -708,7 +703,7 @@ static u64 find_root_gen(int fd) memset(&args, 0, sizeof(args)); - sk->tree_id = 1; + sk->tree_id = BTRFS_ROOT_TREE_OBJECTID; /* * there may be more than one ROOT_ITEM key if there are @@ -726,8 +721,7 @@ static u64 find_root_gen(int fd) while (1) { ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args); if (ret < 0) { - fprintf(stderr, "ERROR: can't perform the search - %s\n", - strerror(errno)); + error("can't perform the search: %s", strerror(errno)); return 0; } /* the ioctl returns the number of item it found in nr_items */ @@ -787,7 +781,7 @@ static char *__ino_resolve(int fd, u64 dirid) ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args); if (ret < 0) { - fprintf(stderr, "ERROR: Failed to lookup path for dirid %llu - %s\n", + error("failed to lookup path for dirid %llu: %s", (unsigned long long)dirid, strerror(errno)); return ERR_PTR(ret); } @@ -813,9 +807,10 @@ static char *__ino_resolve(int fd, u64 dirid) * simple string builder, returning a new string with both * dirid and name */ -static char *build_name(char *dirid, char *name) +static char *build_name(const char *dirid, const char *name) { char *full; + if (!dirid) return strdup(name); @@ -865,8 +860,7 @@ static char *ino_resolve(int fd, u64 ino, u64 *cache_dirid, char **cache_name) ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args); if (ret < 0) { - fprintf(stderr, "ERROR: can't perform the search - %s\n", - strerror(errno)); + error("can't perform the search: %s", strerror(errno)); return NULL; } /* the ioctl returns the number of item it found in nr_items */ @@ -925,7 +919,7 @@ int btrfs_list_get_default_subvolume(int fd, u64 *default_id) * search for a dir item with a name 'default' in the tree of * tree roots, it should point us to a default root */ - sk->tree_id = 1; + sk->tree_id = BTRFS_ROOT_TREE_OBJECTID; /* don't worry about ancient format and request only one item */ sk->nr_items = 1; @@ -965,7 +959,7 @@ out: return 0; } -static int __list_subvol_search(int fd, struct root_lookup *root_lookup) +static int list_subvol_search(int fd, struct root_lookup *root_lookup) { int ret; struct btrfs_ioctl_search_args args; @@ -973,7 +967,7 @@ static int __list_subvol_search(int fd, struct root_lookup *root_lookup) struct btrfs_ioctl_search_header sh; struct btrfs_root_ref *ref; struct btrfs_root_item *ri; - unsigned long off = 0; + unsigned long off; int name_len; char *name; u64 dir_id; @@ -981,42 +975,24 @@ static int __list_subvol_search(int fd, struct root_lookup *root_lookup) u64 ogen; u64 flags; int i; - time_t t; - u8 uuid[BTRFS_UUID_SIZE]; - u8 puuid[BTRFS_UUID_SIZE]; - u8 ruuid[BTRFS_UUID_SIZE]; - root_lookup_init(root_lookup); + root_lookup->root.rb_node = NULL; memset(&args, 0, sizeof(args)); - /* search in the tree of tree roots */ - sk->tree_id = 1; - - /* - * set the min and max to backref keys. The search will - * only send back this type of key now. - */ - sk->max_type = BTRFS_ROOT_BACKREF_KEY; + sk->tree_id = BTRFS_ROOT_TREE_OBJECTID; + /* Search both live and deleted subvolumes */ sk->min_type = BTRFS_ROOT_ITEM_KEY; - - sk->min_objectid = BTRFS_FIRST_FREE_OBJECTID; - - /* - * set all the other params to the max, we'll take any objectid - * and any trans - */ + sk->max_type = BTRFS_ROOT_BACKREF_KEY; + sk->min_objectid = BTRFS_FS_TREE_OBJECTID; sk->max_objectid = BTRFS_LAST_FREE_OBJECTID; sk->max_offset = (u64)-1; sk->max_transid = (u64)-1; - /* just a big number, doesn't matter much */ - sk->nr_items = 4096; - while(1) { + sk->nr_items = 4096; ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args); if (ret < 0) return ret; - /* the ioctl returns the number of item it found in nr_items */ if (sk->nr_items == 0) break; @@ -1035,22 +1011,29 @@ static int __list_subvol_search(int fd, struct root_lookup *root_lookup) name = (char *)(ref + 1); dir_id = btrfs_stack_root_ref_dirid(ref); - add_root(root_lookup, sh.objectid, sh.offset, - 0, 0, dir_id, name, name_len, 0, 0, 0, - NULL, NULL, NULL); - } else if (sh.type == BTRFS_ROOT_ITEM_KEY) { + add_root_backref(root_lookup, sh.objectid, + sh.offset, dir_id, name, + name_len); + } else if (sh.type == BTRFS_ROOT_ITEM_KEY && + (sh.objectid >= BTRFS_FIRST_FREE_OBJECTID || + sh.objectid == BTRFS_FS_TREE_OBJECTID)) { + time_t otime; + u8 uuid[BTRFS_UUID_SIZE]; + u8 puuid[BTRFS_UUID_SIZE]; + u8 ruuid[BTRFS_UUID_SIZE]; + ri = (struct btrfs_root_item *)(args.buf + off); gen = btrfs_root_generation(ri); flags = btrfs_root_flags(ri); if(sh.len > sizeof(struct btrfs_root_item_v0)) { - t = btrfs_stack_timespec_sec(&ri->otime); + otime = btrfs_stack_timespec_sec(&ri->otime); ogen = btrfs_root_otransid(ri); memcpy(uuid, ri->uuid, BTRFS_UUID_SIZE); memcpy(puuid, ri->parent_uuid, BTRFS_UUID_SIZE); memcpy(ruuid, ri->received_uuid, BTRFS_UUID_SIZE); } else { - t = 0; + otime = 0; ogen = 0; memset(uuid, 0, BTRFS_UUID_SIZE); memset(puuid, 0, BTRFS_UUID_SIZE); @@ -1059,22 +1042,17 @@ static int __list_subvol_search(int fd, struct root_lookup *root_lookup) add_root(root_lookup, sh.objectid, 0, sh.offset, flags, 0, NULL, 0, ogen, - gen, t, uuid, puuid, ruuid); + gen, otime, uuid, puuid, ruuid); } off += sh.len; - /* - * record the mins in sk so we can make sure the - * next search doesn't repeat this root - */ sk->min_objectid = sh.objectid; sk->min_type = sh.type; sk->min_offset = sh.offset; } - sk->nr_items = 4096; sk->min_offset++; - if (!sk->min_offset) /* overflow */ + if (!sk->min_offset) sk->min_type++; else continue; @@ -1209,20 +1187,19 @@ struct btrfs_list_filter_set *btrfs_list_alloc_filter_set(void) return set; } -void btrfs_list_free_filter_set(struct btrfs_list_filter_set *filter_set) -{ - free(filter_set); -} - -int btrfs_list_setup_filter(struct btrfs_list_filter_set **filter_set, +/* + * Setup list filters. Exit if there's not enough memory, as we can't continue + * without the structures set up properly. + */ +void btrfs_list_setup_filter(struct btrfs_list_filter_set **filter_set, enum btrfs_list_filter_enum filter, u64 data) { struct btrfs_list_filter_set *set = *filter_set; int size; - BUG_ON(!set); - BUG_ON(filter >= BTRFS_LIST_FILTER_MAX); - BUG_ON(set->nfilters > set->total); + ASSERT(set != NULL); + ASSERT(filter < BTRFS_LIST_FILTER_MAX); + ASSERT(set->nfilters <= set->total); if (set->nfilters == set->total) { void *tmp; @@ -1244,7 +1221,7 @@ int btrfs_list_setup_filter(struct btrfs_list_filter_set **filter_set, *filter_set = set; } - BUG_ON(set->filters[set->nfilters].filter_func); + ASSERT(set->filters[set->nfilters].filter_func == NULL); if (filter == BTRFS_LIST_FILTER_DELETED) set->only_deleted = 1; @@ -1252,7 +1229,6 @@ int btrfs_list_setup_filter(struct btrfs_list_filter_set **filter_set, set->filters[set->nfilters].filter_func = all_filter_funcs[filter]; set->filters[set->nfilters].data = data; set->nfilters++; - return 0; } static int filter_root(struct root_info *ri, @@ -1279,7 +1255,7 @@ static int filter_root(struct root_info *ri, return 1; } -static void __filter_and_sort_subvol(struct root_lookup *all_subvols, +static void filter_and_sort_subvol(struct root_lookup *all_subvols, struct root_lookup *sort_tree, struct btrfs_list_filter_set *filter_set, struct btrfs_list_comparer_set *comp_set, @@ -1289,7 +1265,7 @@ static void __filter_and_sort_subvol(struct root_lookup *all_subvols, struct root_info *entry; int ret; - root_lookup_init(sort_tree); + sort_tree->root.rb_node = NULL; n = rb_last(&all_subvols->root); while (n) { @@ -1307,7 +1283,7 @@ static void __filter_and_sort_subvol(struct root_lookup *all_subvols, } } -static int __list_subvol_fill_paths(int fd, struct root_lookup *root_lookup) +static int list_subvol_fill_paths(int fd, struct root_lookup *root_lookup) { struct rb_node *n; @@ -1331,7 +1307,7 @@ static void print_subvolume_column(struct root_info *subv, char tstr[256]; char uuidparse[BTRFS_UUID_UNPARSED_SIZE]; - BUG_ON(column >= BTRFS_LIST_ALL || column < 0); + ASSERT(0 <= column && column < BTRFS_LIST_ALL); switch (column) { case BTRFS_LIST_OBJECTID: @@ -1389,7 +1365,8 @@ static void print_subvolume_column(struct root_info *subv, } } -static void print_single_volume_info_raw(struct root_info *subv, char *raw_prefix) +static void print_one_subvol_info_raw(struct root_info *subv, + const char *raw_prefix) { int i; @@ -1405,7 +1382,7 @@ static void print_single_volume_info_raw(struct root_info *subv, char *raw_prefi printf("\n"); } -static void print_single_volume_info_table(struct root_info *subv) +static void print_one_subvol_info_table(struct root_info *subv) { int i; @@ -1424,7 +1401,7 @@ static void print_single_volume_info_table(struct root_info *subv) printf("\n"); } -static void print_single_volume_info_default(struct root_info *subv) +static void print_one_subvol_info_default(struct root_info *subv) { int i; @@ -1441,7 +1418,7 @@ static void print_single_volume_info_default(struct root_info *subv) printf("\n"); } -static void print_all_volume_info_tab_head(void) +static void print_all_subvol_info_tab_head(void) { int i; int len; @@ -1470,27 +1447,27 @@ static void print_all_volume_info_tab_head(void) } } -static void print_all_volume_info(struct root_lookup *sorted_tree, - int layout, char *raw_prefix) +static void print_all_subvol_info(struct root_lookup *sorted_tree, + enum btrfs_list_layout layout, const char *raw_prefix) { struct rb_node *n; struct root_info *entry; if (layout == BTRFS_LIST_LAYOUT_TABLE) - print_all_volume_info_tab_head(); + print_all_subvol_info_tab_head(); n = rb_first(&sorted_tree->root); while (n) { entry = rb_entry(n, struct root_info, sort_node); switch (layout) { case BTRFS_LIST_LAYOUT_DEFAULT: - print_single_volume_info_default(entry); + print_one_subvol_info_default(entry); break; case BTRFS_LIST_LAYOUT_TABLE: - print_single_volume_info_table(entry); + print_one_subvol_info_table(entry); break; case BTRFS_LIST_LAYOUT_RAW: - print_single_volume_info_raw(entry, raw_prefix); + print_one_subvol_info_raw(entry, raw_prefix); break; } n = rb_next(n); @@ -1501,10 +1478,9 @@ static int btrfs_list_subvols(int fd, struct root_lookup *root_lookup) { int ret; - ret = __list_subvol_search(fd, root_lookup); + ret = list_subvol_search(fd, root_lookup); if (ret) { - fprintf(stderr, "ERROR: can't perform the search - %s\n", - strerror(errno)); + error("can't perform the search: %s", strerror(errno)); return ret; } @@ -1512,13 +1488,14 @@ static int btrfs_list_subvols(int fd, struct root_lookup *root_lookup) * now we have an rbtree full of root_info objects, but we need to fill * in their path names within the subvol that is referencing each one. */ - ret = __list_subvol_fill_paths(fd, root_lookup); + ret = list_subvol_fill_paths(fd, root_lookup); return ret; } int btrfs_list_subvols_print(int fd, struct btrfs_list_filter_set *filter_set, struct btrfs_list_comparer_set *comp_set, - int layout, int full_path, char *raw_prefix) + enum btrfs_list_layout layout, int full_path, + const char *raw_prefix) { struct root_lookup root_lookup; struct root_lookup root_sort; @@ -1533,11 +1510,11 @@ int btrfs_list_subvols_print(int fd, struct btrfs_list_filter_set *filter_set, ret = btrfs_list_subvols(fd, &root_lookup); if (ret) return ret; - __filter_and_sort_subvol(&root_lookup, &root_sort, filter_set, + filter_and_sort_subvol(&root_lookup, &root_sort, filter_set, comp_set, top_id); - print_all_volume_info(&root_sort, layout, raw_prefix); - __free_all_subvolumn(&root_lookup); + print_all_subvol_info(&root_sort, layout, raw_prefix); + rb_free_nodes(&root_lookup.root, free_root_info); return 0; } @@ -1549,6 +1526,37 @@ static char *strdup_or_null(const char *s) return strdup(s); } +int btrfs_get_toplevel_subvol(int fd, struct root_info *the_ri) +{ + int ret; + struct root_lookup rl; + struct rb_node *rbn; + struct root_info *ri; + u64 root_id; + + ret = btrfs_list_get_path_rootid(fd, &root_id); + if (ret) + return ret; + + ret = btrfs_list_subvols(fd, &rl); + if (ret) + return ret; + + rbn = rb_first(&rl.root); + ri = rb_entry(rbn, struct root_info, rb_node); + + if (ri->root_id != BTRFS_FS_TREE_OBJECTID) + return -ENOENT; + + memcpy(the_ri, ri, offsetof(struct root_info, path)); + the_ri->path = strdup_or_null("/"); + the_ri->name = strdup_or_null("<FS_TREE>"); + the_ri->full_path = strdup_or_null("/"); + rb_free_nodes(&rl.root, free_root_info); + + return ret; +} + int btrfs_get_subvol(int fd, struct root_info *the_ri) { int ret, rr; @@ -1584,7 +1592,7 @@ int btrfs_get_subvol(int fd, struct root_info *the_ri) } rbn = rb_next(rbn); } - __free_all_subvolumn(&rl); + rb_free_nodes(&rl.root, free_root_info); return ret; } @@ -1631,8 +1639,8 @@ static int print_one_extent(int fd, struct btrfs_ioctl_search_header *sh, disk_offset = 0; len = btrfs_stack_file_extent_ram_bytes(item); } else { - printf("unhandled extent type %d for inode %llu " - "file offset %llu gen %llu\n", + error( + "unhandled extent type %d for inode %llu file offset %llu gen %llu", type, (unsigned long long)btrfs_search_header_objectid(sh), (unsigned long long)btrfs_search_header_offset(sh), @@ -1706,8 +1714,7 @@ int btrfs_list_find_updated_files(int fd, u64 root_id, u64 oldest_gen) while(1) { ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args); if (ret < 0) { - fprintf(stderr, "ERROR: can't perform the search - %s\n", - strerror(errno)); + error("can't perform the search: %s", strerror(errno)); break; } /* the ioctl returns the number of item it found in nr_items */ @@ -1778,11 +1785,11 @@ char *btrfs_list_path_for_root(int fd, u64 root) if (ret) return ERR_PTR(ret); - ret = __list_subvol_search(fd, &root_lookup); + ret = list_subvol_search(fd, &root_lookup); if (ret < 0) return ERR_PTR(ret); - ret = __list_subvol_fill_paths(fd, &root_lookup); + ret = list_subvol_fill_paths(fd, &root_lookup); if (ret < 0) return ERR_PTR(ret); @@ -1803,7 +1810,7 @@ char *btrfs_list_path_for_root(int fd, u64 root) n = rb_prev(n); } - __free_all_subvolumn(&root_lookup); + rb_free_nodes(&root_lookup.root, free_root_info); return ret_path; } @@ -1897,19 +1904,12 @@ int btrfs_list_parse_filter_string(char *opt_arg, int btrfs_list_get_path_rootid(int fd, u64 *treeid) { - int ret; - struct btrfs_ioctl_ino_lookup_args args; - - memset(&args, 0, sizeof(args)); - args.objectid = BTRFS_FIRST_FREE_OBJECTID; + int ret; - ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args); - if (ret < 0) { - fprintf(stderr, - "ERROR: can't perform the search - %s\n", + ret = lookup_path_rootid(fd, treeid); + if (ret < 0) + error("cannot resolve rootid for path: %s", strerror(errno)); - return ret; - } - *treeid = args.treeid; - return 0; + + return ret; } diff --git a/btrfs-list.h b/btrfs-list.h index 13f44c3a..6e5fc778 100644 --- a/btrfs-list.h +++ b/btrfs-list.h @@ -31,9 +31,11 @@ #include <time.h> -#define BTRFS_LIST_LAYOUT_DEFAULT 0 -#define BTRFS_LIST_LAYOUT_TABLE 1 -#define BTRFS_LIST_LAYOUT_RAW 2 +enum btrfs_list_layout { + BTRFS_LIST_LAYOUT_DEFAULT = 0, + BTRFS_LIST_LAYOUT_TABLE, + BTRFS_LIST_LAYOUT_RAW +}; /* * one of these for each root we find. @@ -160,19 +162,19 @@ int btrfs_list_parse_filter_string(char *optarg, enum btrfs_list_filter_enum type); void btrfs_list_setup_print_column(enum btrfs_list_column_enum column); struct btrfs_list_filter_set *btrfs_list_alloc_filter_set(void); -void btrfs_list_free_filter_set(struct btrfs_list_filter_set *filter_set); -int btrfs_list_setup_filter(struct btrfs_list_filter_set **filter_set, +void btrfs_list_setup_filter(struct btrfs_list_filter_set **filter_set, enum btrfs_list_filter_enum filter, u64 data); struct btrfs_list_comparer_set *btrfs_list_alloc_comparer_set(void); -void btrfs_list_free_comparer_set(struct btrfs_list_comparer_set *comp_set); int btrfs_list_subvols_print(int fd, struct btrfs_list_filter_set *filter_set, struct btrfs_list_comparer_set *comp_set, - int is_tab_result, int full_path, char *raw_prefix); + enum btrfs_list_layout layot, int full_path, + const char *raw_prefix); int btrfs_list_find_updated_files(int fd, u64 root_id, u64 oldest_gen); int btrfs_list_get_default_subvolume(int fd, u64 *default_id); char *btrfs_list_path_for_root(int fd, u64 root); int btrfs_list_get_path_rootid(int fd, u64 *treeid); int btrfs_get_subvol(int fd, struct root_info *the_ri); +int btrfs_get_toplevel_subvol(int fd, struct root_info *the_ri); #endif diff --git a/btrfs-map-logical.c b/btrfs-map-logical.c index f3be1ea8..e49a735e 100644 --- a/btrfs-map-logical.c +++ b/btrfs-map-logical.c @@ -125,7 +125,7 @@ static int __print_mapping_info(struct btrfs_fs_info *fs_info, u64 logical, multi->stripes[0].physical, device->name); } - kfree(multi); + free(multi); multi = NULL; cur_offset += cur_len; } diff --git a/btrfs-show-super.c b/btrfs-show-super.c index 3e2ceb58..f97f83ba 100644 --- a/btrfs-show-super.c +++ b/btrfs-show-super.c @@ -27,6 +27,9 @@ int main(int argc, char **argv) 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); diff --git a/btrfstune.c b/btrfstune.c index b824f2b8..e8e3d00a 100644 --- a/btrfstune.c +++ b/btrfstune.c @@ -50,18 +50,18 @@ static int update_seeding_flag(struct btrfs_root *root, int set_flag) if (force) return 0; else - fprintf(stderr, "seeding flag is already set on %s\n", device); + warning("seeding flag is already set on %s", + device); return 1; } super_flags |= BTRFS_SUPER_FLAG_SEEDING; } else { if (!(super_flags & BTRFS_SUPER_FLAG_SEEDING)) { - fprintf(stderr, "seeding flag is not set on %s\n", - device); + warning("seeding flag is not set on %s", device); return 1; } super_flags &= ~BTRFS_SUPER_FLAG_SEEDING; - fprintf(stderr, "Warning: Seeding flag cleared.\n"); + warning("seeding flag cleared on %s", device); } trans = btrfs_start_transaction(root, 1); @@ -118,19 +118,16 @@ static int change_header_uuid(struct btrfs_root *root, struct extent_buffer *eb) static int change_extents_uuid(struct btrfs_fs_info *fs_info) { struct btrfs_root *root = fs_info->extent_root; - struct btrfs_path *path; + struct btrfs_path path; struct btrfs_key key = {0, 0, 0}; int ret = 0; - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - + btrfs_init_path(&path); /* * Here we don't use transaction as it will takes a lot of reserve * space, and that will make a near-full btrfs unable to change uuid */ - ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); + ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0); if (ret < 0) goto out; @@ -140,33 +137,32 @@ static int change_extents_uuid(struct btrfs_fs_info *fs_info) u64 flags; u64 bytenr; - btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); + btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]); if (key.type != BTRFS_EXTENT_ITEM_KEY && key.type != BTRFS_METADATA_ITEM_KEY) goto next; - ei = btrfs_item_ptr(path->nodes[0], path->slots[0], + ei = btrfs_item_ptr(path.nodes[0], path.slots[0], struct btrfs_extent_item); - flags = btrfs_extent_flags(path->nodes[0], ei); + flags = btrfs_extent_flags(path.nodes[0], ei); if (!(flags & BTRFS_EXTENT_FLAG_TREE_BLOCK)) goto next; bytenr = key.objectid; eb = read_tree_block(root, bytenr, root->nodesize, 0); if (IS_ERR(eb)) { - fprintf(stderr, "Failed to read tree block: %llu\n", - bytenr); + error("failed to read tree block: %llu", bytenr); ret = PTR_ERR(eb); goto out; } ret = change_header_uuid(root, eb); free_extent_buffer(eb); if (ret < 0) { - fprintf(stderr, "Failed to change uuid of tree block: %llu\n", + error("failed to change uuid of tree block: %llu", bytenr); goto out; } next: - ret = btrfs_next_item(root, path); + ret = btrfs_next_item(root, &path); if (ret < 0) goto out; if (ret > 0) { @@ -176,7 +172,7 @@ next: } out: - btrfs_free_path(path); + btrfs_release_path(&path); return ret; } @@ -204,28 +200,26 @@ static int change_device_uuid(struct btrfs_root *root, struct extent_buffer *eb, static int change_devices_uuid(struct btrfs_fs_info *fs_info) { struct btrfs_root *root = fs_info->chunk_root; - struct btrfs_path *path; + struct btrfs_path path; struct btrfs_key key = {0, 0, 0}; int ret = 0; - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; + btrfs_init_path(&path); /* No transaction again */ - ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); + ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0); if (ret < 0) goto out; while (1) { - btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); + btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]); if (key.type != BTRFS_DEV_ITEM_KEY || key.objectid != BTRFS_DEV_ITEMS_OBJECTID) goto next; - ret = change_device_uuid(root, path->nodes[0], path->slots[0]); + ret = change_device_uuid(root, path.nodes[0], path.slots[0]); if (ret < 0) goto out; next: - ret = btrfs_next_item(root, path); + ret = btrfs_next_item(root, &path); if (ret < 0) goto out; if (ret > 0) { @@ -234,7 +228,7 @@ next: } } out: - btrfs_free_path(path); + btrfs_release_path(&path); return ret; } @@ -310,8 +304,8 @@ static int change_uuid(struct btrfs_fs_info *fs_info, const char *new_fsid_str) uuid_parse(new_fsid_str, tmp); if (memcmp(tmp, new_fsid, BTRFS_FSID_SIZE)) { - fprintf(stderr, - "ERROR: New fsid %s is not the same with unfinished fsid change\n", + error( + "new fsid %s is not the same with unfinished fsid change", new_fsid_str); return -EINVAL; } @@ -343,7 +337,7 @@ static int change_uuid(struct btrfs_fs_info *fs_info, const char *new_fsid_str) printf("Change fsid in extents\n"); ret = change_extents_uuid(fs_info); if (ret < 0) { - fprintf(stderr, "Failed to change UUID of metadata\n"); + error("failed to change UUID of metadata: %d", ret); goto out; } @@ -351,7 +345,7 @@ static int change_uuid(struct btrfs_fs_info *fs_info, const char *new_fsid_str) printf("Change fsid on devices\n"); ret = change_devices_uuid(fs_info); if (ret < 0) { - fprintf(stderr, "Failed to change UUID of devices\n"); + error("failed to change UUID of devices: %d", ret); goto out; } @@ -448,13 +442,11 @@ int main(int argc, char *argv[]) } if (random_fsid && new_fsid_str) { - fprintf(stderr, - "ERROR: Random fsid can't be used with specified fsid\n"); + error("random fsid can't be used with specified fsid"); return 1; } if (!super_flags && !seeding_flag && !(random_fsid || new_fsid_str)) { - fprintf(stderr, - "ERROR: At least one option should be assigned.\n"); + error("at least one option should be specified"); print_usage(); return 1; } @@ -464,39 +456,36 @@ int main(int argc, char *argv[]) ret = uuid_parse(new_fsid_str, tmp); if (ret < 0) { - fprintf(stderr, - "ERROR: Could not parse UUID: %s\n", - new_fsid_str); + error("could not parse UUID: %s", new_fsid_str); return 1; } if (!test_uuid_unique(new_fsid_str)) { - fprintf(stderr, - "ERROR: Fsid %s is not unique\n", - new_fsid_str); + error("fsid %s is not unique", new_fsid_str); return 1; } } ret = check_mounted(device); if (ret < 0) { - fprintf(stderr, "Could not check mount status: %s\n", + error("could not check mount status of %s: %s", device, strerror(-ret)); return 1; } else if (ret) { - fprintf(stderr, "%s is mounted\n", device); + error("%s is mounted", device); return 1; } root = open_ctree(device, 0, ctree_flags); if (!root) { - fprintf(stderr, "Open ctree failed\n"); + error("open ctree failed"); return 1; } if (seeding_flag) { if (!seeding_value && !force) { - fprintf(stderr, "Warning: This is dangerous, clearing the seeding flag may cause the derived device not to be mountable!\n"); + warning( +"this is dangerous, clearing the seeding flag may cause the derived device not to be mountable!"); ret = ask_user("We are going to clear the seeding flag, are you sure?"); if (!ret) { fprintf(stderr, "Clear seeding flag canceled\n"); @@ -520,10 +509,10 @@ int main(int argc, char *argv[]) if (random_fsid || new_fsid_str) { if (!force) { - fprintf(stderr, - "Warning: It's highly recommended to run 'btrfs check' before this operation\n"); - fprintf(stderr, - "Also canceling running UUID change progress may cause corruption\n"); + warning( + "it's highly recommended to run 'btrfs check' before this operation"); + warning( + "also canceling running UUID change progress may cause corruption"); ret = ask_user("We are going to change UUID, are your sure?"); if (!ret) { fprintf(stderr, "UUID change canceled\n"); @@ -542,7 +531,7 @@ int main(int argc, char *argv[]) } else { root->fs_info->readonly = 1; ret = 1; - fprintf(stderr, "btrfstune failed\n"); + error("btrfstune failed"); } out: close_ctree(root); diff --git a/chunk-recover.c b/chunk-recover.c index 4a081db2..e6b26ac3 100644 --- a/chunk-recover.c +++ b/chunk-recover.c @@ -968,7 +968,7 @@ static int build_device_map_by_chunk_record(struct btrfs_root *root, map->stripes[i].dev = btrfs_find_device(root, devid, uuid, NULL); if (!map->stripes[i].dev) { - kfree(map); + free(map); return -EIO; } } @@ -1398,26 +1398,24 @@ static int rebuild_block_group(struct btrfs_trans_handle *trans, { struct chunk_record *chunk_rec; struct btrfs_key search_key; - struct btrfs_path *path; + struct btrfs_path path; u64 used = 0; int ret = 0; if (list_empty(&rc->rebuild_chunks)) return 0; - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; + btrfs_init_path(&path); list_for_each_entry(chunk_rec, &rc->rebuild_chunks, list) { search_key.objectid = chunk_rec->offset; search_key.type = BTRFS_EXTENT_ITEM_KEY; search_key.offset = 0; ret = btrfs_search_slot(NULL, root->fs_info->extent_root, - &search_key, path, 0, 0); + &search_key, &path, 0, 0); if (ret < 0) goto out; ret = calculate_bg_used(root->fs_info->extent_root, - chunk_rec, path, &used); + chunk_rec, &path, &used); /* * Extent tree is damaged, better to rebuild the whole extent * tree. Currently, change the used to chunk's len to prevent @@ -1432,7 +1430,7 @@ static int rebuild_block_group(struct btrfs_trans_handle *trans, "Mark the block group full to prevent block rsv problems\n"); used = chunk_rec->length; } - btrfs_release_path(path); + btrfs_release_path(&path); ret = __insert_block_group(trans, chunk_rec, root->fs_info->extent_root, used); @@ -1440,7 +1438,7 @@ static int rebuild_block_group(struct btrfs_trans_handle *trans, goto out; } out: - btrfs_free_path(path); + btrfs_release_path(&path); return ret; } @@ -1479,7 +1477,7 @@ open_ctree_with_broken_chunk(struct recover_control *rc) memcpy(fs_info->fsid, &disk_super->fsid, BTRFS_FSID_SIZE); - ret = btrfs_check_fs_compatibility(disk_super, 1); + ret = btrfs_check_fs_compatibility(disk_super, OPEN_CTREE_WRITES); if (ret) goto out_devices; @@ -1488,7 +1486,7 @@ open_ctree_with_broken_chunk(struct recover_control *rc) sectorsize = btrfs_super_sectorsize(disk_super); stripesize = btrfs_super_stripesize(disk_super); - __setup_root(nodesize, leafsize, sectorsize, stripesize, + btrfs_setup_root(nodesize, leafsize, sectorsize, stripesize, fs_info->chunk_root, fs_info, BTRFS_CHUNK_TREE_OBJECTID); ret = build_device_maps_by_chunk_records(rc, fs_info->chunk_root); @@ -1914,7 +1912,7 @@ static int check_one_csum(int fd, u64 start, u32 len, u32 tree_csum) } ret = 0; csum_result = btrfs_csum_data(NULL, data, csum_result, len); - btrfs_csum_final(csum_result, (char *)&csum_result); + btrfs_csum_final(csum_result, (u8 *)&csum_result); if (csum_result != tree_csum) ret = 1; out: @@ -1947,9 +1945,12 @@ static int insert_stripe(struct list_head *devexts, dev = btrfs_find_device_by_devid(rc->fs_devices, devext->objectid, 0); if (!dev) - return 1; - BUG_ON(btrfs_find_device_by_devid(rc->fs_devices, devext->objectid, - 1)); + return -ENOENT; + if (btrfs_find_device_by_devid(rc->fs_devices, devext->objectid, 1)) { + error("unexpected: found another device with id %llu", + (unsigned long long)devext->objectid); + return -EINVAL; + } chunk->stripes[index].devid = devext->objectid; chunk->stripes[index].offset = devext->offset; @@ -2247,6 +2248,13 @@ static int btrfs_recover_chunks(struct recover_control *rc) chunk->sub_stripes = calc_sub_nstripes(bg->flags); ret = insert_cache_extent(&rc->chunk, &chunk->cache); + if (ret == -EEXIST) { + error("duplicate entry in cache start %llu size %llu", + (unsigned long long)chunk->cache.start, + (unsigned long long)chunk->cache.size); + free(chunk); + return ret; + } BUG_ON(ret); list_del_init(&bg->list); diff --git a/cmds-check.c b/cmds-check.c index df97d3b6..37e5ff18 100644 --- a/cmds-check.c +++ b/cmds-check.c @@ -41,6 +41,7 @@ #include "rbtree-utils.h" #include "backref.h" #include "ulist.h" +#include "hash.h" enum task_position { TASK_EXTENTS, @@ -112,6 +113,24 @@ struct data_backref { u32 found_ref; }; +#define ROOT_DIR_ERROR (1<<1) /* bad ROOT_DIR */ +#define DIR_ITEM_MISSING (1<<2) /* DIR_ITEM not found */ +#define DIR_ITEM_MISMATCH (1<<3) /* DIR_ITEM found but not match */ +#define INODE_REF_MISSING (1<<4) /* INODE_REF/INODE_EXTREF not found */ +#define INODE_ITEM_MISSING (1<<5) /* INODE_ITEM not found */ +#define INODE_ITEM_MISMATCH (1<<6) /* INODE_ITEM found but not match */ +#define FILE_EXTENT_ERROR (1<<7) /* bad FILE_EXTENT */ +#define ODD_CSUM_ITEM (1<<8) /* CSUM_ITEM error */ +#define CSUM_ITEM_MISSING (1<<9) /* CSUM_ITEM not found */ +#define LINK_COUNT_ERROR (1<<10) /* INODE_ITEM nlink count error */ +#define NBYTES_ERROR (1<<11) /* INODE_ITEM nbytes count error */ +#define ISIZE_ERROR (1<<12) /* INODE_ITEM size count error */ +#define ORPHAN_ITEM (1<<13) /* INODE_ITEM no reference */ +#define NO_INODE_ITEM (1<<14) /* no inode_item */ +#define LAST_ITEM (1<<15) /* Complete this tree traversal */ +#define ROOT_REF_MISSING (1<<16) /* ROOT_REF not found */ +#define ROOT_REF_MISMATCH (1<<17) /* ROOT_REF found but not match */ + static inline struct data_backref* to_data_backref(struct extent_backref *back) { return container_of(back, struct data_backref, node); @@ -185,9 +204,9 @@ struct inode_backref { unsigned int found_dir_item:1; unsigned int found_dir_index:1; unsigned int found_inode_ref:1; - unsigned int filetype:8; + u8 filetype; + u8 ref_type; int errors; - unsigned int ref_type; u64 dir; u64 index; u16 namelen; @@ -659,6 +678,7 @@ static struct inode_record *clone_inode_rec(struct inode_record *orig_rec) struct inode_backref *tmp; struct orphan_data_extent *src_orphan; struct orphan_data_extent *dst_orphan; + struct rb_node *rb; size_t size; int ret; @@ -691,10 +711,21 @@ static struct inode_record *clone_inode_rec(struct inode_record *orig_rec) list_add_tail(&dst_orphan->list, &rec->orphan_extents); } ret = copy_file_extent_holes(&rec->holes, &orig_rec->holes); - BUG_ON(ret < 0); + if (ret < 0) + goto cleanup_rb; return rec; +cleanup_rb: + rb = rb_first(&rec->holes); + while (rb) { + struct file_extent_hole *hole; + + hole = rb_entry(rb, struct file_extent_hole, node); + rb = rb_next(rb); + free(hole); + } + cleanup: if (!list_empty(&rec->backrefs)) list_for_each_entry_safe(orig, tmp, &rec->backrefs, list) { @@ -924,7 +955,7 @@ static void maybe_free_inode_rec(struct cache_tree *inode_cache, struct cache_extent *cache; struct inode_backref *tmp, *backref; struct ptr_node *node; - unsigned char filetype; + u8 filetype; if (!rec->found_inode_item) return; @@ -1055,7 +1086,7 @@ static struct inode_backref *get_inode_backref(struct inode_record *rec, static int add_inode_backref(struct cache_tree *inode_cache, u64 ino, u64 dir, u64 index, const char *name, int namelen, - int filetype, int itemtype, int errors) + u8 filetype, u8 itemtype, int errors) { struct inode_record *rec; struct inode_backref *backref; @@ -1458,7 +1489,7 @@ static int process_dir_item(struct btrfs_root *root, u32 data_len; int error; int nritems = 0; - int filetype; + u8 filetype; struct btrfs_dir_item *di; struct inode_record *rec; struct cache_tree *root_cache; @@ -1826,6 +1857,96 @@ static int process_one_leaf(struct btrfs_root *root, struct extent_buffer *eb, return ret; } +struct node_refs { + u64 bytenr[BTRFS_MAX_LEVEL]; + u64 refs[BTRFS_MAX_LEVEL]; + int need_check[BTRFS_MAX_LEVEL]; +}; + +static int update_nodes_refs(struct btrfs_root *root, u64 bytenr, + struct node_refs *nrefs, u64 level); +static int check_inode_item(struct btrfs_root *root, struct btrfs_path *path, + unsigned int ext_ref); + +static int process_one_leaf_v2(struct btrfs_root *root, struct btrfs_path *path, + struct node_refs *nrefs, int *level, int ext_ref) +{ + struct extent_buffer *cur = path->nodes[0]; + struct btrfs_key key; + u64 cur_bytenr; + u32 nritems; + u64 first_ino = 0; + int root_level = btrfs_header_level(root->node); + int i; + int ret = 0; /* Final return value */ + int err = 0; /* Positive error bitmap */ + + cur_bytenr = cur->start; + + /* skip to first inode item or the first inode number change */ + nritems = btrfs_header_nritems(cur); + for (i = 0; i < nritems; i++) { + btrfs_item_key_to_cpu(cur, &key, i); + if (i == 0) + first_ino = key.objectid; + if (key.type == BTRFS_INODE_ITEM_KEY || + (first_ino && first_ino != key.objectid)) + break; + } + if (i == nritems) { + path->slots[0] = nritems; + return 0; + } + path->slots[0] = i; + +again: + err |= check_inode_item(root, path, ext_ref); + + if (err & LAST_ITEM) + goto out; + + /* still have inode items in thie leaf */ + if (cur->start == cur_bytenr) + goto again; + + /* + * we have switched to another leaf, above nodes may + * have changed, here walk down the path, if a node + * or leaf is shared, check whether we can skip this + * node or leaf. + */ + for (i = root_level; i >= 0; i--) { + if (path->nodes[i]->start == nrefs->bytenr[i]) + continue; + + ret = update_nodes_refs(root, + path->nodes[i]->start, + nrefs, i); + if (ret) + goto out; + + if (!nrefs->need_check[i]) { + *level += 1; + break; + } + } + + for (i = 0; i < *level; i++) { + free_extent_buffer(path->nodes[i]); + path->nodes[i] = NULL; + } +out: + err &= ~LAST_ITEM; + /* + * Convert any error bitmap to -EIO, as we should avoid + * mixing positive and negative return value to represent + * error + */ + if (err && !ret) + ret = -EIO; + return ret; +} + static void reada_walk_down(struct btrfs_root *root, struct extent_buffer *node, int slot) { @@ -1899,10 +2020,66 @@ static int check_child_node(struct btrfs_root *root, return ret; } -struct node_refs { - u64 bytenr[BTRFS_MAX_LEVEL]; - u64 refs[BTRFS_MAX_LEVEL]; -}; +/* + * for a tree node or leaf, if it's shared, indeed we don't need to iterate it + * in every fs or file tree check. Here we find its all root ids, and only check + * it in the fs or file tree which has the smallest root id. + */ +static int need_check(struct btrfs_root *root, struct ulist *roots) +{ + struct rb_node *node; + struct ulist_node *u; + + if (roots->nnodes == 1) + return 1; + + node = rb_first(&roots->root); + u = rb_entry(node, struct ulist_node, rb_node); + /* + * current root id is not smallest, we skip it and let it be checked + * in the fs or file tree who hash the smallest root id. + */ + if (root->objectid != u->val) + return 0; + + return 1; +} + +/* + * for a tree node or leaf, we record its reference count, so later if we still + * process this node or leaf, don't need to compute its reference count again. + */ +static int update_nodes_refs(struct btrfs_root *root, u64 bytenr, + struct node_refs *nrefs, u64 level) +{ + int check, ret; + u64 refs; + struct ulist *roots; + + if (nrefs->bytenr[level] != bytenr) { + ret = btrfs_lookup_extent_info(NULL, root, bytenr, + level, 1, &refs, NULL); + if (ret < 0) + return ret; + + nrefs->bytenr[level] = bytenr; + nrefs->refs[level] = refs; + if (refs > 1) { + ret = btrfs_find_all_roots(NULL, root->fs_info, bytenr, + 0, &roots); + if (ret) + return -EIO; + + check = need_check(root, roots); + ulist_free(roots); + nrefs->need_check[level] = check; + } else { + nrefs->need_check[level] = 1; + } + } + + return 0; +} static int walk_down_tree(struct btrfs_root *root, struct btrfs_path *path, struct walk_control *wc, int *level, @@ -2033,6 +2210,110 @@ out: return err; } +static int check_inode_item(struct btrfs_root *root, struct btrfs_path *path, + unsigned int ext_ref); + +static int walk_down_tree_v2(struct btrfs_root *root, struct btrfs_path *path, + int *level, struct node_refs *nrefs, int ext_ref) +{ + enum btrfs_tree_block_status status; + u64 bytenr; + u64 ptr_gen; + struct extent_buffer *next; + struct extent_buffer *cur; + u32 blocksize; + int ret; + + WARN_ON(*level < 0); + WARN_ON(*level >= BTRFS_MAX_LEVEL); + + ret = update_nodes_refs(root, path->nodes[*level]->start, + nrefs, *level); + if (ret < 0) + return ret; + + while (*level >= 0) { + WARN_ON(*level < 0); + WARN_ON(*level >= BTRFS_MAX_LEVEL); + cur = path->nodes[*level]; + + if (btrfs_header_level(cur) != *level) + WARN_ON(1); + + if (path->slots[*level] >= btrfs_header_nritems(cur)) + break; + /* Don't forgot to check leaf/node validation */ + if (*level == 0) { + ret = btrfs_check_leaf(root, NULL, cur); + if (ret != BTRFS_TREE_BLOCK_CLEAN) { + ret = -EIO; + break; + } + ret = process_one_leaf_v2(root, path, nrefs, + level, ext_ref); + break; + } else { + ret = btrfs_check_node(root, NULL, cur); + if (ret != BTRFS_TREE_BLOCK_CLEAN) { + ret = -EIO; + break; + } + } + bytenr = btrfs_node_blockptr(cur, path->slots[*level]); + ptr_gen = btrfs_node_ptr_generation(cur, path->slots[*level]); + blocksize = root->nodesize; + + ret = update_nodes_refs(root, bytenr, nrefs, *level - 1); + if (ret) + break; + if (!nrefs->need_check[*level - 1]) { + path->slots[*level]++; + continue; + } + + next = btrfs_find_tree_block(root, bytenr, blocksize); + if (!next || !btrfs_buffer_uptodate(next, ptr_gen)) { + free_extent_buffer(next); + reada_walk_down(root, cur, path->slots[*level]); + next = read_tree_block(root, bytenr, blocksize, + ptr_gen); + if (!extent_buffer_uptodate(next)) { + struct btrfs_key node_key; + + btrfs_node_key_to_cpu(path->nodes[*level], + &node_key, + path->slots[*level]); + btrfs_add_corrupt_extent_record(root->fs_info, + &node_key, + path->nodes[*level]->start, + root->nodesize, *level); + ret = -EIO; + break; + } + } + + ret = check_child_node(root, cur, path->slots[*level], next); + if (ret < 0) + break; + + if (btrfs_is_leaf(next)) + status = btrfs_check_leaf(root, NULL, next); + else + status = btrfs_check_node(root, NULL, next); + if (status != BTRFS_TREE_BLOCK_CLEAN) { + free_extent_buffer(next); + ret = -EIO; + break; + } + + *level = *level - 1; + free_extent_buffer(path->nodes[*level]); + path->nodes[*level] = next; + path->slots[*level] = 0; + } + return ret; +} + static int walk_up_tree(struct btrfs_root *root, struct btrfs_path *path, struct walk_control *wc, int *level) { @@ -2057,6 +2338,27 @@ static int walk_up_tree(struct btrfs_root *root, struct btrfs_path *path, return 1; } +static int walk_up_tree_v2(struct btrfs_root *root, struct btrfs_path *path, + int *level) +{ + int i; + struct extent_buffer *leaf; + + for (i = *level; i < BTRFS_MAX_LEVEL - 1 && path->nodes[i]; i++) { + leaf = path->nodes[i]; + if (path->slots[i] + 1 < btrfs_header_nritems(leaf)) { + path->slots[i]++; + *level = i; + return 0; + } else { + free_extent_buffer(path->nodes[*level]); + path->nodes[*level] = NULL; + *level = i + 1; + } + } + return 1; +} + static int check_root_dir(struct inode_record *rec) { struct inode_backref *backref; @@ -2174,7 +2476,7 @@ static int add_missing_dir_index(struct btrfs_root *root, struct inode_record *rec, struct inode_backref *backref) { - struct btrfs_path *path; + struct btrfs_path path; struct btrfs_trans_handle *trans; struct btrfs_dir_item *dir_item; struct extent_buffer *leaf; @@ -2185,27 +2487,22 @@ static int add_missing_dir_index(struct btrfs_root *root, u32 data_size = sizeof(*dir_item) + backref->namelen; int ret; - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - trans = btrfs_start_transaction(root, 1); - if (IS_ERR(trans)) { - btrfs_free_path(path); + if (IS_ERR(trans)) return PTR_ERR(trans); - } fprintf(stderr, "repairing missing dir index item for inode %llu\n", (unsigned long long)rec->ino); + + btrfs_init_path(&path); key.objectid = backref->dir; key.type = BTRFS_DIR_INDEX_KEY; key.offset = backref->index; - - ret = btrfs_insert_empty_item(trans, root, path, &key, data_size); + ret = btrfs_insert_empty_item(trans, root, &path, &key, data_size); BUG_ON(ret); - leaf = path->nodes[0]; - dir_item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_dir_item); + leaf = path.nodes[0]; + dir_item = btrfs_item_ptr(leaf, path.slots[0], struct btrfs_dir_item); disk_key.objectid = cpu_to_le64(rec->ino); disk_key.type = BTRFS_INODE_ITEM_KEY; @@ -2218,7 +2515,7 @@ static int add_missing_dir_index(struct btrfs_root *root, name_ptr = (unsigned long)(dir_item + 1); write_extent_buffer(leaf, backref->name, name_ptr, backref->namelen); btrfs_mark_buffer_dirty(leaf); - btrfs_free_path(path); + btrfs_release_path(&path); btrfs_commit_transaction(trans, root); backref->found_dir_index = 1; @@ -2243,31 +2540,25 @@ static int delete_dir_index(struct btrfs_root *root, { struct btrfs_trans_handle *trans; struct btrfs_dir_item *di; - struct btrfs_path *path; + struct btrfs_path path; int ret = 0; - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - trans = btrfs_start_transaction(root, 1); - if (IS_ERR(trans)) { - btrfs_free_path(path); + if (IS_ERR(trans)) return PTR_ERR(trans); - } - fprintf(stderr, "Deleting bad dir index [%llu,%u,%llu] root %llu\n", (unsigned long long)backref->dir, BTRFS_DIR_INDEX_KEY, (unsigned long long)backref->index, (unsigned long long)root->objectid); - di = btrfs_lookup_dir_index(trans, root, path, backref->dir, + btrfs_init_path(&path); + di = btrfs_lookup_dir_index(trans, root, &path, backref->dir, backref->name, backref->namelen, backref->index, -1); if (IS_ERR(di)) { ret = PTR_ERR(di); - btrfs_free_path(path); + btrfs_release_path(&path); btrfs_commit_transaction(trans, root); if (ret == -ENOENT) return 0; @@ -2275,11 +2566,11 @@ static int delete_dir_index(struct btrfs_root *root, } if (!di) - ret = btrfs_del_item(trans, root, path); + ret = btrfs_del_item(trans, root, &path); else - ret = btrfs_delete_one_dir_name(trans, root, path, di); + ret = btrfs_delete_one_dir_name(trans, root, &path, di); BUG_ON(ret); - btrfs_free_path(path); + btrfs_release_path(&path); btrfs_commit_transaction(trans, root); return ret; } @@ -2562,6 +2853,31 @@ out: return ret; } +static int get_highest_inode(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, + u64 *highest_ino) +{ + struct btrfs_key key, found_key; + int ret; + + btrfs_init_path(path); + key.objectid = BTRFS_LAST_FREE_OBJECTID; + key.offset = -1; + key.type = BTRFS_INODE_ITEM_KEY; + ret = btrfs_search_slot(trans, root, &key, path, -1, 1); + if (ret == 1) { + btrfs_item_key_to_cpu(path->nodes[0], &found_key, + path->slots[0] - 1); + *highest_ino = found_key.objectid; + ret = 0; + } + if (*highest_ino >= BTRFS_LAST_FREE_OBJECTID) + ret = -EOVERFLOW; + btrfs_release_path(path); + return ret; +} + static int repair_inode_nlinks(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_path *path, @@ -2607,11 +2923,9 @@ static int repair_inode_nlinks(struct btrfs_trans_handle *trans, } if (rec->found_link == 0) { - lost_found_ino = root->highest_inode; - if (lost_found_ino >= BTRFS_LAST_FREE_OBJECTID) { - ret = -EOVERFLOW; + ret = get_highest_inode(trans, root, path, &lost_found_ino); + if (ret < 0) goto out; - } lost_found_ino++; ret = btrfs_mkdir(trans, root, dir_name, strlen(dir_name), BTRFS_FIRST_FREE_OBJECTID, &lost_found_ino, @@ -2682,48 +2996,46 @@ out: */ static int find_normal_file_extent(struct btrfs_root *root, u64 ino) { - struct btrfs_path *path; + struct btrfs_path path; struct btrfs_key key; struct btrfs_key found_key; struct btrfs_file_extent_item *fi; u8 type; int ret = 0; - path = btrfs_alloc_path(); - if (!path) - goto out; + btrfs_init_path(&path); key.objectid = ino; key.type = BTRFS_EXTENT_DATA_KEY; key.offset = 0; - ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); + ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0); if (ret < 0) { ret = 0; goto out; } - if (ret && path->slots[0] >= btrfs_header_nritems(path->nodes[0])) { - ret = btrfs_next_leaf(root, path); + if (ret && path.slots[0] >= btrfs_header_nritems(path.nodes[0])) { + ret = btrfs_next_leaf(root, &path); if (ret) { ret = 0; goto out; } } while (1) { - btrfs_item_key_to_cpu(path->nodes[0], &found_key, - path->slots[0]); + btrfs_item_key_to_cpu(path.nodes[0], &found_key, + path.slots[0]); if (found_key.objectid != ino || found_key.type != BTRFS_EXTENT_DATA_KEY) break; - fi = btrfs_item_ptr(path->nodes[0], path->slots[0], + fi = btrfs_item_ptr(path.nodes[0], path.slots[0], struct btrfs_file_extent_item); - type = btrfs_file_extent_type(path->nodes[0], fi); + type = btrfs_file_extent_type(path.nodes[0], fi); if (type != BTRFS_FILE_EXTENT_INLINE) { ret = 1; goto out; } } out: - btrfs_free_path(path); + btrfs_release_path(&path); return ret; } @@ -2914,7 +3226,7 @@ out: static int try_repair_inode(struct btrfs_root *root, struct inode_record *rec) { struct btrfs_trans_handle *trans; - struct btrfs_path *path; + struct btrfs_path path; int ret = 0; if (!(rec->errors & (I_ERR_DIR_ISIZE_WRONG | @@ -2926,10 +3238,6 @@ static int try_repair_inode(struct btrfs_root *root, struct inode_record *rec) I_ERR_FILE_NBYTES_WRONG))) return rec->errors; - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - /* * For nlink repair, it may create a dir and add link, so * 2 for parent(256)'s dir_index and dir_item @@ -2938,27 +3246,26 @@ static int try_repair_inode(struct btrfs_root *root, struct inode_record *rec) * 2 for lost+found dir's dir_index and dir_item for the file */ trans = btrfs_start_transaction(root, 7); - if (IS_ERR(trans)) { - btrfs_free_path(path); + if (IS_ERR(trans)) return PTR_ERR(trans); - } + btrfs_init_path(&path); if (rec->errors & I_ERR_NO_INODE_ITEM) - ret = repair_inode_no_item(trans, root, path, rec); + ret = repair_inode_no_item(trans, root, &path, rec); if (!ret && rec->errors & I_ERR_FILE_EXTENT_ORPHAN) - ret = repair_inode_orphan_extent(trans, root, path, rec); + ret = repair_inode_orphan_extent(trans, root, &path, rec); if (!ret && rec->errors & I_ERR_FILE_EXTENT_DISCOUNT) - ret = repair_inode_discount_extent(trans, root, path, rec); + ret = repair_inode_discount_extent(trans, root, &path, rec); if (!ret && rec->errors & I_ERR_DIR_ISIZE_WRONG) - ret = repair_inode_isize(trans, root, path, rec); + ret = repair_inode_isize(trans, root, &path, rec); if (!ret && rec->errors & I_ERR_NO_ORPHAN_ITEM) - ret = repair_inode_orphan_item(trans, root, path, rec); + ret = repair_inode_orphan_item(trans, root, &path, rec); if (!ret && rec->errors & I_ERR_LINK_COUNT_WRONG) - ret = repair_inode_nlinks(trans, root, path, rec); + ret = repair_inode_nlinks(trans, root, &path, rec); if (!ret && rec->errors & I_ERR_FILE_NBYTES_WRONG) - ret = repair_inode_nbytes(trans, root, path, rec); + ret = repair_inode_nbytes(trans, root, &path, rec); btrfs_commit_transaction(trans, root); - btrfs_free_path(path); + btrfs_release_path(&path); return ret; } @@ -2982,21 +3289,6 @@ static int check_inode_recs(struct btrfs_root *root, } /* - * We need to record the highest inode number for later 'lost+found' - * dir creation. - * We must select an ino not used/referred by any existing inode, or - * 'lost+found' ino may be a missing ino in a corrupted leaf, - * this may cause 'lost+found' dir has wrong nlinks. - */ - cache = last_cache_extent(inode_cache); - if (cache) { - node = container_of(cache, struct ptr_node, cache); - rec = node->data; - if (rec->ino > root->highest_inode) - root->highest_inode = rec->ino; - } - - /* * We need to repair backrefs first because we could change some of the * errors in the inode recs. * @@ -3210,7 +3502,7 @@ static void free_root_record(struct cache_extent *cache) free(backref); } - kfree(rec); + free(rec); } FREE_EXTENT_CACHE_BASED_TREE(root_recs, free_root_record); @@ -3491,7 +3783,7 @@ static int repair_btree(struct btrfs_root *root, struct cache_tree *corrupt_blocks) { struct btrfs_trans_handle *trans; - struct btrfs_path *path; + struct btrfs_path path; struct btrfs_corrupt_block *corrupt; struct cache_extent *cache; struct btrfs_key key; @@ -3502,23 +3794,20 @@ static int repair_btree(struct btrfs_root *root, if (cache_tree_empty(corrupt_blocks)) return 0; - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - trans = btrfs_start_transaction(root, 1); if (IS_ERR(trans)) { ret = PTR_ERR(trans); fprintf(stderr, "Error starting transaction: %s\n", strerror(-ret)); - goto out_free_path; + return ret; } + btrfs_init_path(&path); cache = first_cache_extent(corrupt_blocks); while (cache) { corrupt = container_of(cache, struct btrfs_corrupt_block, cache); level = corrupt->level; - path->lowest_level = level; + path.lowest_level = level; key.objectid = corrupt->key.objectid; key.type = corrupt->key.type; key.offset = corrupt->key.offset; @@ -3529,22 +3818,22 @@ static int repair_btree(struct btrfs_root *root, * so ins_len set to 0 here. * Balance will be done after all corrupt node/leaf is deleted. */ - ret = btrfs_search_slot(trans, root, &key, path, 0, 1); + ret = btrfs_search_slot(trans, root, &key, &path, 0, 1); if (ret < 0) goto out; - offset = btrfs_node_blockptr(path->nodes[level], - path->slots[level]); + offset = btrfs_node_blockptr(path.nodes[level], + path.slots[level]); /* Remove the ptr */ - ret = btrfs_del_ptr(trans, root, path, level, - path->slots[level]); + ret = btrfs_del_ptr(trans, root, &path, level, + path.slots[level]); if (ret < 0) goto out; /* * Remove the corresponding extent * return value is not concerned. */ - btrfs_release_path(path); + btrfs_release_path(&path); ret = btrfs_free_extent(trans, root, offset, root->nodesize, 0, root->root_key.objectid, level - 1, 0); @@ -3557,18 +3846,17 @@ static int repair_btree(struct btrfs_root *root, corrupt = container_of(cache, struct btrfs_corrupt_block, cache); memcpy(&key, &corrupt->key, sizeof(key)); - ret = btrfs_search_slot(trans, root, &key, path, -1, 1); + ret = btrfs_search_slot(trans, root, &key, &path, -1, 1); if (ret < 0) goto out; /* return will always >0 since it won't find the item */ ret = 0; - btrfs_release_path(path); + btrfs_release_path(&path); cache = next_cache_extent(cache); } out: btrfs_commit_transaction(trans, root); -out_free_path: - btrfs_free_path(path); + btrfs_release_path(&path); return ret; } @@ -3836,6 +4124,1100 @@ out: return err; } +/* + * Find DIR_ITEM/DIR_INDEX for the given key and check it with the specified + * INODE_REF/INODE_EXTREF match. + * + * @root: the root of the fs/file tree + * @ref_key: the key of the INODE_REF/INODE_EXTREF + * @key: the key of the DIR_ITEM/DIR_INDEX + * @index: the index in the INODE_REF/INODE_EXTREF, be used to + * distinguish root_dir between normal dir/file + * @name: the name in the INODE_REF/INODE_EXTREF + * @namelen: the length of name in the INODE_REF/INODE_EXTREF + * @mode: the st_mode of INODE_ITEM + * + * Return 0 if no error occurred. + * Return ROOT_DIR_ERROR if found DIR_ITEM/DIR_INDEX for root_dir. + * Return DIR_ITEM_MISSING if couldn't find DIR_ITEM/DIR_INDEX for normal + * dir/file. + * Return DIR_ITEM_MISMATCH if INODE_REF/INODE_EXTREF and DIR_ITEM/DIR_INDEX + * not match for normal dir/file. + */ +static int find_dir_item(struct btrfs_root *root, struct btrfs_key *ref_key, + struct btrfs_key *key, u64 index, char *name, + u32 namelen, u32 mode) +{ + struct btrfs_path path; + struct extent_buffer *node; + struct btrfs_dir_item *di; + struct btrfs_key location; + char namebuf[BTRFS_NAME_LEN] = {0}; + u32 total; + u32 cur = 0; + u32 len; + u32 name_len; + u32 data_len; + u8 filetype; + int slot; + int ret; + + btrfs_init_path(&path); + ret = btrfs_search_slot(NULL, root, key, &path, 0, 0); + if (ret < 0) { + ret = DIR_ITEM_MISSING; + goto out; + } + + /* Process root dir and goto out*/ + if (index == 0) { + if (ret == 0) { + ret = ROOT_DIR_ERROR; + error( + "root %llu INODE %s[%llu %llu] ROOT_DIR shouldn't have %s", + root->objectid, + ref_key->type == BTRFS_INODE_REF_KEY ? + "REF" : "EXTREF", + ref_key->objectid, ref_key->offset, + key->type == BTRFS_DIR_ITEM_KEY ? + "DIR_ITEM" : "DIR_INDEX"); + } else { + ret = 0; + } + + goto out; + } + + /* Process normal file/dir */ + if (ret > 0) { + ret = DIR_ITEM_MISSING; + error( + "root %llu INODE %s[%llu %llu] doesn't have related %s[%llu %llu] namelen %u filename %s filetype %d", + root->objectid, + ref_key->type == BTRFS_INODE_REF_KEY ? "REF" : "EXTREF", + ref_key->objectid, ref_key->offset, + key->type == BTRFS_DIR_ITEM_KEY ? + "DIR_ITEM" : "DIR_INDEX", + key->objectid, key->offset, namelen, name, + imode_to_type(mode)); + goto out; + } + + /* Check whether inode_id/filetype/name match */ + node = path.nodes[0]; + slot = path.slots[0]; + di = btrfs_item_ptr(node, slot, struct btrfs_dir_item); + total = btrfs_item_size_nr(node, slot); + while (cur < total) { + ret = DIR_ITEM_MISMATCH; + name_len = btrfs_dir_name_len(node, di); + data_len = btrfs_dir_data_len(node, di); + + btrfs_dir_item_key_to_cpu(node, di, &location); + if (location.objectid != ref_key->objectid || + location.type != BTRFS_INODE_ITEM_KEY || + location.offset != 0) + goto next; + + filetype = btrfs_dir_type(node, di); + if (imode_to_type(mode) != filetype) + goto next; + + if (name_len <= BTRFS_NAME_LEN) { + len = name_len; + } else { + len = BTRFS_NAME_LEN; + warning("root %llu %s[%llu %llu] name too long %u, trimmed", + root->objectid, + key->type == BTRFS_DIR_ITEM_KEY ? + "DIR_ITEM" : "DIR_INDEX", + key->objectid, key->offset, name_len); + } + read_extent_buffer(node, namebuf, (unsigned long)(di + 1), len); + if (len != namelen || strncmp(namebuf, name, len)) + goto next; + + ret = 0; + goto out; +next: + len = sizeof(*di) + name_len + data_len; + di = (struct btrfs_dir_item *)((char *)di + len); + cur += len; + } + if (ret == DIR_ITEM_MISMATCH) + error( + "root %llu INODE %s[%llu %llu] and %s[%llu %llu] mismatch namelen %u filename %s filetype %d", + root->objectid, + ref_key->type == BTRFS_INODE_REF_KEY ? "REF" : "EXTREF", + ref_key->objectid, ref_key->offset, + key->type == BTRFS_DIR_ITEM_KEY ? + "DIR_ITEM" : "DIR_INDEX", + key->objectid, key->offset, namelen, name, + imode_to_type(mode)); +out: + btrfs_release_path(&path); + return ret; +} + +/* + * Traverse the given INODE_REF and call find_dir_item() to find related + * DIR_ITEM/DIR_INDEX. + * + * @root: the root of the fs/file tree + * @ref_key: the key of the INODE_REF + * @refs: the count of INODE_REF + * @mode: the st_mode of INODE_ITEM + * + * Return 0 if no error occurred. + */ +static int check_inode_ref(struct btrfs_root *root, struct btrfs_key *ref_key, + struct extent_buffer *node, int slot, u64 *refs, + int mode) +{ + struct btrfs_key key; + struct btrfs_inode_ref *ref; + char namebuf[BTRFS_NAME_LEN] = {0}; + u32 total; + u32 cur = 0; + u32 len; + u32 name_len; + u64 index; + int ret, err = 0; + + ref = btrfs_item_ptr(node, slot, struct btrfs_inode_ref); + total = btrfs_item_size_nr(node, slot); + +next: + /* Update inode ref count */ + (*refs)++; + + index = btrfs_inode_ref_index(node, ref); + name_len = btrfs_inode_ref_name_len(node, ref); + if (name_len <= BTRFS_NAME_LEN) { + len = name_len; + } else { + len = BTRFS_NAME_LEN; + warning("root %llu INODE_REF[%llu %llu] name too long", + root->objectid, ref_key->objectid, ref_key->offset); + } + + read_extent_buffer(node, namebuf, (unsigned long)(ref + 1), len); + + /* Check root dir ref name */ + if (index == 0 && strncmp(namebuf, "..", name_len)) { + error("root %llu INODE_REF[%llu %llu] ROOT_DIR name shouldn't be %s", + root->objectid, ref_key->objectid, ref_key->offset, + namebuf); + err |= ROOT_DIR_ERROR; + } + + /* Find related DIR_INDEX */ + key.objectid = ref_key->offset; + key.type = BTRFS_DIR_INDEX_KEY; + key.offset = index; + ret = find_dir_item(root, ref_key, &key, index, namebuf, len, mode); + err |= ret; + + /* Find related dir_item */ + key.objectid = ref_key->offset; + key.type = BTRFS_DIR_ITEM_KEY; + key.offset = btrfs_name_hash(namebuf, len); + ret = find_dir_item(root, ref_key, &key, index, namebuf, len, mode); + err |= ret; + + len = sizeof(*ref) + name_len; + ref = (struct btrfs_inode_ref *)((char *)ref + len); + cur += len; + if (cur < total) + goto next; + + return err; +} + +/* + * Traverse the given INODE_EXTREF and call find_dir_item() to find related + * DIR_ITEM/DIR_INDEX. + * + * @root: the root of the fs/file tree + * @ref_key: the key of the INODE_EXTREF + * @refs: the count of INODE_EXTREF + * @mode: the st_mode of INODE_ITEM + * + * Return 0 if no error occurred. + */ +static int check_inode_extref(struct btrfs_root *root, + struct btrfs_key *ref_key, + struct extent_buffer *node, int slot, u64 *refs, + int mode) +{ + struct btrfs_key key; + struct btrfs_inode_extref *extref; + char namebuf[BTRFS_NAME_LEN] = {0}; + u32 total; + u32 cur = 0; + u32 len; + u32 name_len; + u64 index; + u64 parent; + int ret; + int err = 0; + + extref = btrfs_item_ptr(node, slot, struct btrfs_inode_extref); + total = btrfs_item_size_nr(node, slot); + +next: + /* update inode ref count */ + (*refs)++; + name_len = btrfs_inode_extref_name_len(node, extref); + index = btrfs_inode_extref_index(node, extref); + parent = btrfs_inode_extref_parent(node, extref); + if (name_len <= BTRFS_NAME_LEN) { + len = name_len; + } else { + len = BTRFS_NAME_LEN; + warning("root %llu INODE_EXTREF[%llu %llu] name too long", + root->objectid, ref_key->objectid, ref_key->offset); + } + read_extent_buffer(node, namebuf, (unsigned long)(extref + 1), len); + + /* Check root dir ref name */ + if (index == 0 && strncmp(namebuf, "..", name_len)) { + error("root %llu INODE_EXTREF[%llu %llu] ROOT_DIR name shouldn't be %s", + root->objectid, ref_key->objectid, ref_key->offset, + namebuf); + err |= ROOT_DIR_ERROR; + } + + /* find related dir_index */ + key.objectid = parent; + key.type = BTRFS_DIR_INDEX_KEY; + key.offset = index; + ret = find_dir_item(root, ref_key, &key, index, namebuf, len, mode); + err |= ret; + + /* find related dir_item */ + key.objectid = parent; + key.type = BTRFS_DIR_ITEM_KEY; + key.offset = btrfs_name_hash(namebuf, len); + ret = find_dir_item(root, ref_key, &key, index, namebuf, len, mode); + err |= ret; + + len = sizeof(*extref) + name_len; + extref = (struct btrfs_inode_extref *)((char *)extref + len); + cur += len; + + if (cur < total) + goto next; + + return err; +} + +/* + * Find INODE_REF/INODE_EXTREF for the given key and check it with the specified + * DIR_ITEM/DIR_INDEX match. + * + * @root: the root of the fs/file tree + * @key: the key of the INODE_REF/INODE_EXTREF + * @name: the name in the INODE_REF/INODE_EXTREF + * @namelen: the length of name in the INODE_REF/INODE_EXTREF + * @index: the index in the INODE_REF/INODE_EXTREF, for DIR_ITEM set index + * to (u64)-1 + * @ext_ref: the EXTENDED_IREF feature + * + * Return 0 if no error occurred. + * Return >0 for error bitmap + */ +static int find_inode_ref(struct btrfs_root *root, struct btrfs_key *key, + char *name, int namelen, u64 index, + unsigned int ext_ref) +{ + struct btrfs_path path; + struct btrfs_inode_ref *ref; + struct btrfs_inode_extref *extref; + struct extent_buffer *node; + char ref_namebuf[BTRFS_NAME_LEN] = {0}; + u32 total; + u32 cur = 0; + u32 len; + u32 ref_namelen; + u64 ref_index; + u64 parent; + u64 dir_id; + int slot; + int ret; + + btrfs_init_path(&path); + ret = btrfs_search_slot(NULL, root, key, &path, 0, 0); + if (ret) { + ret = INODE_REF_MISSING; + goto extref; + } + + node = path.nodes[0]; + slot = path.slots[0]; + + ref = btrfs_item_ptr(node, slot, struct btrfs_inode_ref); + total = btrfs_item_size_nr(node, slot); + + /* Iterate all entry of INODE_REF */ + while (cur < total) { + ret = INODE_REF_MISSING; + + ref_namelen = btrfs_inode_ref_name_len(node, ref); + ref_index = btrfs_inode_ref_index(node, ref); + if (index != (u64)-1 && index != ref_index) + goto next_ref; + + if (ref_namelen <= BTRFS_NAME_LEN) { + len = ref_namelen; + } else { + len = BTRFS_NAME_LEN; + warning("root %llu INODE %s[%llu %llu] name too long", + root->objectid, + key->type == BTRFS_INODE_REF_KEY ? + "REF" : "EXTREF", + key->objectid, key->offset); + } + read_extent_buffer(node, ref_namebuf, (unsigned long)(ref + 1), + len); + + if (len != namelen || strncmp(ref_namebuf, name, len)) + goto next_ref; + + ret = 0; + goto out; +next_ref: + len = sizeof(*ref) + ref_namelen; + ref = (struct btrfs_inode_ref *)((char *)ref + len); + cur += len; + } + +extref: + /* Skip if not support EXTENDED_IREF feature */ + if (!ext_ref) + goto out; + + btrfs_release_path(&path); + btrfs_init_path(&path); + + dir_id = key->offset; + key->type = BTRFS_INODE_EXTREF_KEY; + key->offset = btrfs_extref_hash(dir_id, name, namelen); + + ret = btrfs_search_slot(NULL, root, key, &path, 0, 0); + if (ret) { + ret = INODE_REF_MISSING; + goto out; + } + + node = path.nodes[0]; + slot = path.slots[0]; + + extref = btrfs_item_ptr(node, slot, struct btrfs_inode_extref); + cur = 0; + total = btrfs_item_size_nr(node, slot); + + /* Iterate all entry of INODE_EXTREF */ + while (cur < total) { + ret = INODE_REF_MISSING; + + ref_namelen = btrfs_inode_extref_name_len(node, extref); + ref_index = btrfs_inode_extref_index(node, extref); + parent = btrfs_inode_extref_parent(node, extref); + if (index != (u64)-1 && index != ref_index) + goto next_extref; + + if (parent != dir_id) + goto next_extref; + + if (ref_namelen <= BTRFS_NAME_LEN) { + len = ref_namelen; + } else { + len = BTRFS_NAME_LEN; + warning("root %llu INODE %s[%llu %llu] name too long", + root->objectid, + key->type == BTRFS_INODE_REF_KEY ? + "REF" : "EXTREF", + key->objectid, key->offset); + } + read_extent_buffer(node, ref_namebuf, + (unsigned long)(extref + 1), len); + + if (len != namelen || strncmp(ref_namebuf, name, len)) + goto next_extref; + + ret = 0; + goto out; + +next_extref: + len = sizeof(*extref) + ref_namelen; + extref = (struct btrfs_inode_extref *)((char *)extref + len); + cur += len; + + } +out: + btrfs_release_path(&path); + return ret; +} + +/* + * Traverse the given DIR_ITEM/DIR_INDEX and check related INODE_ITEM and + * call find_inode_ref() to check related INODE_REF/INODE_EXTREF. + * + * @root: the root of the fs/file tree + * @key: the key of the INODE_REF/INODE_EXTREF + * @size: the st_size of the INODE_ITEM + * @ext_ref: the EXTENDED_IREF feature + * + * Return 0 if no error occurred. + */ +static int check_dir_item(struct btrfs_root *root, struct btrfs_key *key, + struct extent_buffer *node, int slot, u64 *size, + unsigned int ext_ref) +{ + struct btrfs_dir_item *di; + struct btrfs_inode_item *ii; + struct btrfs_path path; + struct btrfs_key location; + char namebuf[BTRFS_NAME_LEN] = {0}; + u32 total; + u32 cur = 0; + u32 len; + u32 name_len; + u32 data_len; + u8 filetype; + u32 mode; + u64 index; + int ret; + int err = 0; + + /* + * For DIR_ITEM set index to (u64)-1, so that find_inode_ref + * ignore index check. + */ + index = (key->type == BTRFS_DIR_INDEX_KEY) ? key->offset : (u64)-1; + + di = btrfs_item_ptr(node, slot, struct btrfs_dir_item); + total = btrfs_item_size_nr(node, slot); + + while (cur < total) { + data_len = btrfs_dir_data_len(node, di); + if (data_len) + error("root %llu %s[%llu %llu] data_len shouldn't be %u", + root->objectid, key->type == BTRFS_DIR_ITEM_KEY ? + "DIR_ITEM" : "DIR_INDEX", + key->objectid, key->offset, data_len); + + name_len = btrfs_dir_name_len(node, di); + if (name_len <= BTRFS_NAME_LEN) { + len = name_len; + } else { + len = BTRFS_NAME_LEN; + warning("root %llu %s[%llu %llu] name too long", + root->objectid, + key->type == BTRFS_DIR_ITEM_KEY ? + "DIR_ITEM" : "DIR_INDEX", + key->objectid, key->offset); + } + (*size) += name_len; + + read_extent_buffer(node, namebuf, (unsigned long)(di + 1), len); + filetype = btrfs_dir_type(node, di); + + btrfs_init_path(&path); + btrfs_dir_item_key_to_cpu(node, di, &location); + + /* Ignore related ROOT_ITEM check */ + if (location.type == BTRFS_ROOT_ITEM_KEY) + goto next; + + /* Check relative INODE_ITEM(existence/filetype) */ + ret = btrfs_search_slot(NULL, root, &location, &path, 0, 0); + if (ret) { + err |= INODE_ITEM_MISSING; + error("root %llu %s[%llu %llu] couldn't find relative INODE_ITEM[%llu] namelen %u filename %s filetype %x", + root->objectid, key->type == BTRFS_DIR_ITEM_KEY ? + "DIR_ITEM" : "DIR_INDEX", key->objectid, + key->offset, location.objectid, name_len, + namebuf, filetype); + goto next; + } + + ii = btrfs_item_ptr(path.nodes[0], path.slots[0], + struct btrfs_inode_item); + mode = btrfs_inode_mode(path.nodes[0], ii); + + if (imode_to_type(mode) != filetype) { + err |= INODE_ITEM_MISMATCH; + error("root %llu %s[%llu %llu] relative INODE_ITEM filetype mismatch namelen %u filename %s filetype %d", + root->objectid, key->type == BTRFS_DIR_ITEM_KEY ? + "DIR_ITEM" : "DIR_INDEX", key->objectid, + key->offset, name_len, namebuf, filetype); + } + + /* Check relative INODE_REF/INODE_EXTREF */ + location.type = BTRFS_INODE_REF_KEY; + location.offset = key->objectid; + ret = find_inode_ref(root, &location, namebuf, len, + index, ext_ref); + err |= ret; + if (ret & INODE_REF_MISSING) + error("root %llu %s[%llu %llu] relative INODE_REF missing namelen %u filename %s filetype %d", + root->objectid, key->type == BTRFS_DIR_ITEM_KEY ? + "DIR_ITEM" : "DIR_INDEX", key->objectid, + key->offset, name_len, namebuf, filetype); + +next: + btrfs_release_path(&path); + len = sizeof(*di) + name_len + data_len; + di = (struct btrfs_dir_item *)((char *)di + len); + cur += len; + + if (key->type == BTRFS_DIR_INDEX_KEY && cur < total) { + error("root %llu DIR_INDEX[%llu %llu] should contain only one entry", + root->objectid, key->objectid, key->offset); + break; + } + } + + return err; +} + +/* + * Check file extent datasum/hole, update the size of the file extents, + * check and update the last offset of the file extent. + * + * @root: the root of fs/file tree. + * @fkey: the key of the file extent. + * @nodatasum: INODE_NODATASUM feature. + * @size: the sum of all EXTENT_DATA items size for this inode. + * @end: the offset of the last extent. + * + * Return 0 if no error occurred. + */ +static int check_file_extent(struct btrfs_root *root, struct btrfs_key *fkey, + struct extent_buffer *node, int slot, + unsigned int nodatasum, u64 *size, u64 *end) +{ + struct btrfs_file_extent_item *fi; + u64 disk_bytenr; + u64 disk_num_bytes; + u64 extent_num_bytes; + u64 found; + unsigned int extent_type; + unsigned int is_hole; + int ret; + int err = 0; + + fi = btrfs_item_ptr(node, slot, struct btrfs_file_extent_item); + + extent_type = btrfs_file_extent_type(node, fi); + /* Skip if file extent is inline */ + if (extent_type == BTRFS_FILE_EXTENT_INLINE) { + struct btrfs_item *e = btrfs_item_nr(slot); + u32 item_inline_len; + + item_inline_len = btrfs_file_extent_inline_item_len(node, e); + extent_num_bytes = btrfs_file_extent_inline_len(node, slot, fi); + if (extent_num_bytes == 0 || + extent_num_bytes != item_inline_len) + err |= FILE_EXTENT_ERROR; + *size += extent_num_bytes; + return err; + } + + /* Check extent type */ + if (extent_type != BTRFS_FILE_EXTENT_REG && + extent_type != BTRFS_FILE_EXTENT_PREALLOC) { + err |= FILE_EXTENT_ERROR; + error("root %llu EXTENT_DATA[%llu %llu] type bad", + root->objectid, fkey->objectid, fkey->offset); + return err; + } + + /* Check REG_EXTENT/PREALLOC_EXTENT */ + disk_bytenr = btrfs_file_extent_disk_bytenr(node, fi); + disk_num_bytes = btrfs_file_extent_disk_num_bytes(node, fi); + extent_num_bytes = btrfs_file_extent_num_bytes(node, fi); + is_hole = (disk_bytenr == 0) && (disk_num_bytes == 0); + + /* Check EXTENT_DATA datasum */ + ret = count_csum_range(root, disk_bytenr, disk_num_bytes, &found); + if (found > 0 && nodatasum) { + err |= ODD_CSUM_ITEM; + error("root %llu EXTENT_DATA[%llu %llu] nodatasum shouldn't have datasum", + root->objectid, fkey->objectid, fkey->offset); + } else if (extent_type == BTRFS_FILE_EXTENT_REG && !nodatasum && + !is_hole && + (ret < 0 || found == 0 || found < disk_num_bytes)) { + err |= CSUM_ITEM_MISSING; + error("root %llu EXTENT_DATA[%llu %llu] datasum missing", + root->objectid, fkey->objectid, fkey->offset); + } else if (extent_type == BTRFS_FILE_EXTENT_PREALLOC && found > 0) { + err |= ODD_CSUM_ITEM; + error("root %llu EXTENT_DATA[%llu %llu] prealloc shouldn't have datasum", + root->objectid, fkey->objectid, fkey->offset); + } + + /* Check EXTENT_DATA hole */ + if (no_holes && is_hole) { + err |= FILE_EXTENT_ERROR; + error("root %llu EXTENT_DATA[%llu %llu] shouldn't be hole", + root->objectid, fkey->objectid, fkey->offset); + } else if (!no_holes && *end != fkey->offset) { + err |= FILE_EXTENT_ERROR; + error("root %llu EXTENT_DATA[%llu %llu] interrupt", + root->objectid, fkey->objectid, fkey->offset); + } + + *end += extent_num_bytes; + if (!is_hole) + *size += extent_num_bytes; + + return err; +} + +/* + * Check INODE_ITEM and related ITEMs (the same inode number) + * 1. check link count + * 2. check inode ref/extref + * 3. check dir item/index + * + * @ext_ref: the EXTENDED_IREF feature + * + * Return 0 if no error occurred. + * Return >0 for error or hit the traversal is done(by error bitmap) + */ +static int check_inode_item(struct btrfs_root *root, struct btrfs_path *path, + unsigned int ext_ref) +{ + struct extent_buffer *node; + struct btrfs_inode_item *ii; + struct btrfs_key key; + u64 inode_id; + u32 mode; + u64 nlink; + u64 nbytes; + u64 isize; + u64 size = 0; + u64 refs = 0; + u64 extent_end = 0; + u64 extent_size = 0; + unsigned int dir; + unsigned int nodatasum; + int slot; + int ret; + int err = 0; + + node = path->nodes[0]; + slot = path->slots[0]; + + btrfs_item_key_to_cpu(node, &key, slot); + inode_id = key.objectid; + + if (inode_id == BTRFS_ORPHAN_OBJECTID) { + ret = btrfs_next_item(root, path); + if (ret > 0) + err |= LAST_ITEM; + return err; + } + + ii = btrfs_item_ptr(node, slot, struct btrfs_inode_item); + isize = btrfs_inode_size(node, ii); + nbytes = btrfs_inode_nbytes(node, ii); + mode = btrfs_inode_mode(node, ii); + dir = imode_to_type(mode) == BTRFS_FT_DIR; + nlink = btrfs_inode_nlink(node, ii); + nodatasum = btrfs_inode_flags(node, ii) & BTRFS_INODE_NODATASUM; + + while (1) { + ret = btrfs_next_item(root, path); + if (ret < 0) { + /* out will fill 'err' rusing current statistics */ + goto out; + } else if (ret > 0) { + err |= LAST_ITEM; + goto out; + } + + node = path->nodes[0]; + slot = path->slots[0]; + btrfs_item_key_to_cpu(node, &key, slot); + if (key.objectid != inode_id) + goto out; + + switch (key.type) { + case BTRFS_INODE_REF_KEY: + ret = check_inode_ref(root, &key, node, slot, &refs, + mode); + err |= ret; + break; + case BTRFS_INODE_EXTREF_KEY: + if (key.type == BTRFS_INODE_EXTREF_KEY && !ext_ref) + warning("root %llu EXTREF[%llu %llu] isn't supported", + root->objectid, key.objectid, + key.offset); + ret = check_inode_extref(root, &key, node, slot, &refs, + mode); + err |= ret; + break; + case BTRFS_DIR_ITEM_KEY: + case BTRFS_DIR_INDEX_KEY: + if (!dir) { + warning("root %llu INODE[%llu] mode %u shouldn't have DIR_INDEX[%llu %llu]", + root->objectid, inode_id, + imode_to_type(mode), key.objectid, + key.offset); + } + ret = check_dir_item(root, &key, node, slot, &size, + ext_ref); + err |= ret; + break; + case BTRFS_EXTENT_DATA_KEY: + if (dir) { + warning("root %llu DIR INODE[%llu] shouldn't EXTENT_DATA[%llu %llu]", + root->objectid, inode_id, key.objectid, + key.offset); + } + ret = check_file_extent(root, &key, node, slot, + nodatasum, &extent_size, + &extent_end); + err |= ret; + break; + case BTRFS_XATTR_ITEM_KEY: + break; + default: + error("ITEM[%llu %u %llu] UNKNOWN TYPE", + key.objectid, key.type, key.offset); + } + } + +out: + /* verify INODE_ITEM nlink/isize/nbytes */ + if (dir) { + if (nlink != 1) { + err |= LINK_COUNT_ERROR; + error("root %llu DIR INODE[%llu] shouldn't have more than one link(%llu)", + root->objectid, inode_id, nlink); + } + + /* + * Just a warning, as dir inode nbytes is just an + * instructive value. + */ + if (!IS_ALIGNED(nbytes, root->nodesize)) { + warning("root %llu DIR INODE[%llu] nbytes should be aligned to %u", + root->objectid, inode_id, root->nodesize); + } + + if (isize != size) { + err |= ISIZE_ERROR; + error("root %llu DIR INODE [%llu] size(%llu) not equal to %llu", + root->objectid, inode_id, isize, size); + } + } else { + if (nlink != refs) { + err |= LINK_COUNT_ERROR; + error("root %llu INODE[%llu] nlink(%llu) not equal to inode_refs(%llu)", + root->objectid, inode_id, nlink, refs); + } else if (!nlink) { + err |= ORPHAN_ITEM; + } + + if (!nbytes && !no_holes && extent_end < isize) { + err |= NBYTES_ERROR; + error("root %llu INODE[%llu] size (%llu) should have a file extent hole", + root->objectid, inode_id, isize); + } + + if (nbytes != extent_size) { + err |= NBYTES_ERROR; + error("root %llu INODE[%llu] nbytes(%llu) not equal to extent_size(%llu)", + root->objectid, inode_id, nbytes, extent_size); + } + } + + return err; +} + +static int check_fs_first_inode(struct btrfs_root *root, unsigned int ext_ref) +{ + struct btrfs_path path; + struct btrfs_key key; + int err = 0; + int ret; + + key.objectid = BTRFS_FIRST_FREE_OBJECTID; + key.type = BTRFS_INODE_ITEM_KEY; + key.offset = 0; + + /* For root being dropped, we don't need to check first inode */ + if (btrfs_root_refs(&root->root_item) == 0 && + btrfs_disk_key_objectid(&root->root_item.drop_progress) >= + key.objectid) + return 0; + + btrfs_init_path(&path); + + ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0); + if (ret < 0) + goto out; + if (ret > 0) { + ret = 0; + err |= INODE_ITEM_MISSING; + } + + err |= check_inode_item(root, &path, ext_ref); + err &= ~LAST_ITEM; + if (err && !ret) + ret = -EIO; +out: + btrfs_release_path(&path); + return ret; +} + +/* + * Iterate all item on the tree and call check_inode_item() to check. + * + * @root: the root of the tree to be checked. + * @ext_ref: the EXTENDED_IREF feature + * + * Return 0 if no error found. + * Return <0 for error. + */ +static int check_fs_root_v2(struct btrfs_root *root, unsigned int ext_ref) +{ + struct btrfs_path path; + struct node_refs nrefs; + struct btrfs_root_item *root_item = &root->root_item; + int ret, wret; + int level; + + /* + * We need to manually check the first inode item(256) + * As the following traversal function will only start from + * the first inode item in the leaf, if inode item(256) is missing + * we will just skip it forever. + */ + ret = check_fs_first_inode(root, ext_ref); + if (ret < 0) + return ret; + + memset(&nrefs, 0, sizeof(nrefs)); + level = btrfs_header_level(root->node); + btrfs_init_path(&path); + + if (btrfs_root_refs(root_item) > 0 || + btrfs_disk_key_objectid(&root_item->drop_progress) == 0) { + path.nodes[level] = root->node; + path.slots[level] = 0; + extent_buffer_get(root->node); + } else { + struct btrfs_key key; + + btrfs_disk_key_to_cpu(&key, &root_item->drop_progress); + level = root_item->drop_level; + path.lowest_level = level; + ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0); + if (ret < 0) + goto out; + ret = 0; + } + + while (1) { + wret = walk_down_tree_v2(root, &path, &level, &nrefs, ext_ref); + if (wret < 0) + ret = wret; + if (wret != 0) + break; + + wret = walk_up_tree_v2(root, &path, &level); + if (wret < 0) + ret = wret; + if (wret != 0) + break; + } + +out: + btrfs_release_path(&path); + return ret; +} + +/* + * Find the relative ref for root_ref and root_backref. + * + * @root: the root of the root tree. + * @ref_key: the key of the root ref. + * + * Return 0 if no error occurred. + */ +static int check_root_ref(struct btrfs_root *root, struct btrfs_key *ref_key, + struct extent_buffer *node, int slot) +{ + struct btrfs_path path; + struct btrfs_key key; + struct btrfs_root_ref *ref; + struct btrfs_root_ref *backref; + char ref_name[BTRFS_NAME_LEN] = {0}; + char backref_name[BTRFS_NAME_LEN] = {0}; + u64 ref_dirid; + u64 ref_seq; + u32 ref_namelen; + u64 backref_dirid; + u64 backref_seq; + u32 backref_namelen; + u32 len; + int ret; + int err = 0; + + ref = btrfs_item_ptr(node, slot, struct btrfs_root_ref); + ref_dirid = btrfs_root_ref_dirid(node, ref); + ref_seq = btrfs_root_ref_sequence(node, ref); + ref_namelen = btrfs_root_ref_name_len(node, ref); + + if (ref_namelen <= BTRFS_NAME_LEN) { + len = ref_namelen; + } else { + len = BTRFS_NAME_LEN; + warning("%s[%llu %llu] ref_name too long", + ref_key->type == BTRFS_ROOT_REF_KEY ? + "ROOT_REF" : "ROOT_BACKREF", ref_key->objectid, + ref_key->offset); + } + read_extent_buffer(node, ref_name, (unsigned long)(ref + 1), len); + + /* Find relative root_ref */ + key.objectid = ref_key->offset; + key.type = BTRFS_ROOT_BACKREF_KEY + BTRFS_ROOT_REF_KEY - ref_key->type; + key.offset = ref_key->objectid; + + btrfs_init_path(&path); + ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0); + if (ret) { + err |= ROOT_REF_MISSING; + error("%s[%llu %llu] couldn't find relative ref", + ref_key->type == BTRFS_ROOT_REF_KEY ? + "ROOT_REF" : "ROOT_BACKREF", + ref_key->objectid, ref_key->offset); + goto out; + } + + backref = btrfs_item_ptr(path.nodes[0], path.slots[0], + struct btrfs_root_ref); + backref_dirid = btrfs_root_ref_dirid(path.nodes[0], backref); + backref_seq = btrfs_root_ref_sequence(path.nodes[0], backref); + backref_namelen = btrfs_root_ref_name_len(path.nodes[0], backref); + + if (backref_namelen <= BTRFS_NAME_LEN) { + len = backref_namelen; + } else { + len = BTRFS_NAME_LEN; + warning("%s[%llu %llu] ref_name too long", + key.type == BTRFS_ROOT_REF_KEY ? + "ROOT_REF" : "ROOT_BACKREF", + key.objectid, key.offset); + } + read_extent_buffer(path.nodes[0], backref_name, + (unsigned long)(backref + 1), len); + + if (ref_dirid != backref_dirid || ref_seq != backref_seq || + ref_namelen != backref_namelen || + strncmp(ref_name, backref_name, len)) { + err |= ROOT_REF_MISMATCH; + error("%s[%llu %llu] mismatch relative ref", + ref_key->type == BTRFS_ROOT_REF_KEY ? + "ROOT_REF" : "ROOT_BACKREF", + ref_key->objectid, ref_key->offset); + } +out: + btrfs_release_path(&path); + return err; +} + +/* + * Check all fs/file tree in low_memory mode. + * + * 1. for fs tree root item, call check_fs_root_v2() + * 2. for fs tree root ref/backref, call check_root_ref() + * + * Return 0 if no error occurred. + */ +static int check_fs_roots_v2(struct btrfs_fs_info *fs_info) +{ + struct btrfs_root *tree_root = fs_info->tree_root; + struct btrfs_root *cur_root = NULL; + struct btrfs_path path; + struct btrfs_key key; + struct extent_buffer *node; + unsigned int ext_ref; + int slot; + int ret; + int err = 0; + + ext_ref = btrfs_fs_incompat(fs_info, EXTENDED_IREF); + + btrfs_init_path(&path); + key.objectid = BTRFS_FS_TREE_OBJECTID; + key.offset = 0; + key.type = BTRFS_ROOT_ITEM_KEY; + + ret = btrfs_search_slot(NULL, tree_root, &key, &path, 0, 0); + if (ret < 0) { + err = ret; + goto out; + } else if (ret > 0) { + err = -ENOENT; + goto out; + } + + while (1) { + node = path.nodes[0]; + slot = path.slots[0]; + btrfs_item_key_to_cpu(node, &key, slot); + if (key.objectid > BTRFS_LAST_FREE_OBJECTID) + goto out; + if (key.type == BTRFS_ROOT_ITEM_KEY && + fs_root_objectid(key.objectid)) { + if (key.objectid == BTRFS_TREE_RELOC_OBJECTID) { + cur_root = btrfs_read_fs_root_no_cache(fs_info, + &key); + } else { + key.offset = (u64)-1; + cur_root = btrfs_read_fs_root(fs_info, &key); + } + + if (IS_ERR(cur_root)) { + error("Fail to read fs/subvol tree: %lld", + key.objectid); + err = -EIO; + goto next; + } + + ret = check_fs_root_v2(cur_root, ext_ref); + err |= ret; + + if (key.objectid == BTRFS_TREE_RELOC_OBJECTID) + btrfs_free_fs_root(cur_root); + } else if (key.type == BTRFS_ROOT_REF_KEY || + key.type == BTRFS_ROOT_BACKREF_KEY) { + ret = check_root_ref(tree_root, &key, node, slot); + err |= ret; + } +next: + ret = btrfs_next_item(tree_root, &path); + if (ret > 0) + goto out; + if (ret < 0) { + err = ret; + goto out; + } + } + +out: + btrfs_release_path(&path); + return err; +} + static int all_backpointers_checked(struct extent_record *rec, int print_errs) { struct list_head *cur = rec->backrefs.next; @@ -4310,7 +5692,7 @@ static int try_to_fix_bad_block(struct btrfs_root *root, struct ulist *roots; struct ulist_node *node; struct btrfs_root *search_root; - struct btrfs_path *path; + struct btrfs_path path; struct ulist_iterator iter; struct btrfs_key root_key, key; int ret; @@ -4319,17 +5701,11 @@ static int try_to_fix_bad_block(struct btrfs_root *root, status != BTRFS_TREE_BLOCK_INVALID_OFFSETS) return -EIO; - path = btrfs_alloc_path(); - if (!path) - return -EIO; - - ret = btrfs_find_all_roots(NULL, root->fs_info, buf->start, - 0, &roots); - if (ret) { - btrfs_free_path(path); + ret = btrfs_find_all_roots(NULL, root->fs_info, buf->start, 0, &roots); + if (ret) return -EIO; - } + btrfs_init_path(&path); ULIST_ITER_INIT(&iter); while ((node = ulist_next(roots, &iter))) { root_key.objectid = node->val; @@ -4349,31 +5725,31 @@ static int try_to_fix_bad_block(struct btrfs_root *root, break; } - path->lowest_level = btrfs_header_level(buf); - path->skip_check_block = 1; - if (path->lowest_level) + path.lowest_level = btrfs_header_level(buf); + path.skip_check_block = 1; + if (path.lowest_level) btrfs_node_key_to_cpu(buf, &key, 0); else btrfs_item_key_to_cpu(buf, &key, 0); - ret = btrfs_search_slot(trans, search_root, &key, path, 0, 1); + ret = btrfs_search_slot(trans, search_root, &key, &path, 0, 1); if (ret) { ret = -EIO; btrfs_commit_transaction(trans, search_root); break; } if (status == BTRFS_TREE_BLOCK_BAD_KEY_ORDER) - ret = fix_key_order(trans, search_root, path); + ret = fix_key_order(trans, search_root, &path); else if (status == BTRFS_TREE_BLOCK_INVALID_OFFSETS) - ret = fix_item_offset(trans, search_root, path); + ret = fix_item_offset(trans, search_root, &path); if (ret) { btrfs_commit_transaction(trans, search_root); break; } - btrfs_release_path(path); + btrfs_release_path(&path); btrfs_commit_transaction(trans, search_root); } ulist_free(roots); - btrfs_free_path(path); + btrfs_release_path(&path); return ret; } @@ -4643,12 +6019,15 @@ static int add_extent_rec_nolookup(struct cache_tree *extent_cache, rec->cache.start = tmpl->start; rec->cache.size = tmpl->nr; ret = insert_cache_extent(extent_cache, &rec->cache); - BUG_ON(ret); + if (ret) { + free(rec); + return ret; + } bytes_used += rec->nr; if (tmpl->metadata) - rec->crossing_stripes = check_crossing_stripes(rec->start, - global_info->tree_root->nodesize); + rec->crossing_stripes = check_crossing_stripes(global_info, + rec->start, global_info->tree_root->nodesize); check_extent_type(rec); return ret; } @@ -4749,7 +6128,8 @@ static int add_extent_rec(struct cache_tree *extent_cache, */ if (tmpl->metadata) rec->crossing_stripes = check_crossing_stripes( - rec->start, global_info->tree_root->nodesize); + global_info, rec->start, + global_info->tree_root->nodesize); check_extent_type(rec); maybe_free_extent_rec(extent_cache, rec); return ret; @@ -4766,6 +6146,7 @@ static int add_tree_backref(struct cache_tree *extent_cache, u64 bytenr, struct extent_record *rec; struct tree_backref *back; struct cache_extent *cache; + int ret; cache = lookup_cache_extent(extent_cache, bytenr, 1); if (!cache) { @@ -4776,7 +6157,9 @@ static int add_tree_backref(struct cache_tree *extent_cache, u64 bytenr, tmpl.nr = 1; tmpl.metadata = 1; - add_extent_rec_nolookup(extent_cache, &tmpl); + ret = add_extent_rec_nolookup(extent_cache, &tmpl); + if (ret) + return ret; /* really a bug in cache_extent implement now */ cache = lookup_cache_extent(extent_cache, bytenr, 1); @@ -4830,6 +6213,7 @@ static int add_data_backref(struct cache_tree *extent_cache, u64 bytenr, struct extent_record *rec; struct data_backref *back; struct cache_extent *cache; + int ret; cache = lookup_cache_extent(extent_cache, bytenr, 1); if (!cache) { @@ -4840,7 +6224,9 @@ static int add_data_backref(struct cache_tree *extent_cache, u64 bytenr, tmpl.nr = 1; tmpl.max_size = max_size; - add_extent_rec_nolookup(extent_cache, &tmpl); + ret = add_extent_rec_nolookup(extent_cache, &tmpl); + if (ret) + return ret; cache = lookup_cache_extent(extent_cache, bytenr, 1); if (!cache) @@ -5474,7 +6860,7 @@ static int check_cache_range(struct btrfs_root *root, continue; if (logical[nr] == offset) { if (stripe_len >= bytes) { - kfree(logical); + free(logical); return 0; } bytes -= stripe_len; @@ -5482,7 +6868,7 @@ static int check_cache_range(struct btrfs_root *root, } else if (logical[nr] < offset) { if (logical[nr] + stripe_len >= offset + bytes) { - kfree(logical); + free(logical); return 0; } bytes = (offset + bytes) - @@ -5505,7 +6891,7 @@ static int check_cache_range(struct btrfs_root *root, offset, logical[nr] - offset); if (ret) { - kfree(logical); + free(logical); return ret; } @@ -5516,7 +6902,7 @@ static int check_cache_range(struct btrfs_root *root, } } - kfree(logical); + free(logical); } entry = btrfs_find_free_space(cache->free_space_ctl, offset, bytes); @@ -5546,31 +6932,27 @@ static int check_cache_range(struct btrfs_root *root, static int verify_space_cache(struct btrfs_root *root, struct btrfs_block_group_cache *cache) { - struct btrfs_path *path; + struct btrfs_path path; struct extent_buffer *leaf; struct btrfs_key key; u64 last; int ret = 0; - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - root = root->fs_info->extent_root; last = max_t(u64, cache->key.objectid, BTRFS_SUPER_INFO_OFFSET); + btrfs_init_path(&path); key.objectid = last; key.offset = 0; key.type = BTRFS_EXTENT_ITEM_KEY; - - ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); + ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0); if (ret < 0) goto out; ret = 0; while (1) { - if (path->slots[0] >= btrfs_header_nritems(path->nodes[0])) { - ret = btrfs_next_leaf(root, path); + if (path.slots[0] >= btrfs_header_nritems(path.nodes[0])) { + ret = btrfs_next_leaf(root, &path); if (ret < 0) goto out; if (ret > 0) { @@ -5578,13 +6960,13 @@ static int verify_space_cache(struct btrfs_root *root, break; } } - leaf = path->nodes[0]; - btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); + leaf = path.nodes[0]; + btrfs_item_key_to_cpu(leaf, &key, path.slots[0]); if (key.objectid >= cache->key.offset + cache->key.objectid) break; if (key.type != BTRFS_EXTENT_ITEM_KEY && key.type != BTRFS_METADATA_ITEM_KEY) { - path->slots[0]++; + path.slots[0]++; continue; } @@ -5593,7 +6975,7 @@ static int verify_space_cache(struct btrfs_root *root, last = key.objectid + key.offset; else last = key.objectid + root->nodesize; - path->slots[0]++; + path.slots[0]++; continue; } @@ -5605,7 +6987,7 @@ static int verify_space_cache(struct btrfs_root *root, last = key.objectid + key.offset; else last = key.objectid + root->nodesize; - path->slots[0]++; + path.slots[0]++; } if (last < cache->key.objectid + cache->key.offset) @@ -5614,7 +6996,7 @@ static int verify_space_cache(struct btrfs_root *root, cache->key.offset - last); out: - btrfs_free_path(path); + btrfs_release_path(&path); if (!ret && !RB_EMPTY_ROOT(&cache->free_space_ctl->free_space_offset)) { @@ -5662,8 +7044,7 @@ static int check_space_cache(struct btrfs_root *root) btrfs_remove_free_space_cache(cache); } - if (btrfs_fs_compat_ro(root->fs_info, - BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE)) { + if (btrfs_fs_compat_ro(root->fs_info, FREE_SPACE_TREE)) { ret = exclude_super_stripes(root, cache); if (ret) { fprintf(stderr, "could not exclude super stripes: %s\n", @@ -5740,7 +7121,7 @@ again: csum = btrfs_csum_data(NULL, (char *)data + tmp, csum, root->sectorsize); - btrfs_csum_final(csum, (char *)&csum); + btrfs_csum_final(csum, (u8 *)&csum); csum_offset = leaf_offset + tmp / root->sectorsize * csum_size; @@ -5771,33 +7152,28 @@ out: static int check_extent_exists(struct btrfs_root *root, u64 bytenr, u64 num_bytes) { - struct btrfs_path *path; + struct btrfs_path path; struct extent_buffer *leaf; struct btrfs_key key; int ret; - path = btrfs_alloc_path(); - if (!path) { - fprintf(stderr, "Error allocating path\n"); - return -ENOMEM; - } - + btrfs_init_path(&path); key.objectid = bytenr; key.type = BTRFS_EXTENT_ITEM_KEY; key.offset = (u64)-1; again: - ret = btrfs_search_slot(NULL, root->fs_info->extent_root, &key, path, + ret = btrfs_search_slot(NULL, root->fs_info->extent_root, &key, &path, 0, 0); if (ret < 0) { fprintf(stderr, "Error looking up extent record %d\n", ret); - btrfs_free_path(path); + btrfs_release_path(&path); return ret; } else if (ret) { - if (path->slots[0] > 0) { - path->slots[0]--; + if (path.slots[0] > 0) { + path.slots[0]--; } else { - ret = btrfs_prev_leaf(root, path); + ret = btrfs_prev_leaf(root, &path); if (ret < 0) { goto out; } else if (ret > 0) { @@ -5807,7 +7183,7 @@ again: } } - btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); + btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]); /* * Block group items come before extent items if they have the same @@ -5818,10 +7194,10 @@ again: * EXTENT_ITEM_KEY please? */ while (key.type > BTRFS_EXTENT_ITEM_KEY) { - if (path->slots[0] > 0) { - path->slots[0]--; + if (path.slots[0] > 0) { + path.slots[0]--; } else { - ret = btrfs_prev_leaf(root, path); + ret = btrfs_prev_leaf(root, &path); if (ret < 0) { goto out; } else if (ret > 0) { @@ -5829,29 +7205,29 @@ again: goto out; } } - btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); + btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]); } while (num_bytes) { - if (path->slots[0] >= btrfs_header_nritems(path->nodes[0])) { - ret = btrfs_next_leaf(root, path); + if (path.slots[0] >= btrfs_header_nritems(path.nodes[0])) { + ret = btrfs_next_leaf(root, &path); if (ret < 0) { fprintf(stderr, "Error going to next leaf " "%d\n", ret); - btrfs_free_path(path); + btrfs_release_path(&path); return ret; } else if (ret) { break; } } - leaf = path->nodes[0]; - btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); + leaf = path.nodes[0]; + btrfs_item_key_to_cpu(leaf, &key, path.slots[0]); if (key.type != BTRFS_EXTENT_ITEM_KEY) { - path->slots[0]++; + path.slots[0]++; continue; } if (key.objectid + key.offset < bytenr) { - path->slots[0]++; + path.slots[0]++; continue; } if (key.objectid > bytenr + num_bytes) @@ -5884,7 +7260,7 @@ again: * in real life, but no harm in coding it up * anyway just in case. */ - btrfs_release_path(path); + btrfs_release_path(&path); ret = check_extent_exists(root, new_start, new_bytes); if (ret) { @@ -5897,7 +7273,7 @@ again: } num_bytes = key.objectid - bytenr; } - path->slots[0]++; + path.slots[0]++; } ret = 0; @@ -5908,13 +7284,13 @@ out: ret = 1; } - btrfs_free_path(path); + btrfs_release_path(&path); return ret; } static int check_csums(struct btrfs_root *root) { - struct btrfs_path *path; + struct btrfs_path path; struct extent_buffer *leaf; struct btrfs_key key; u64 offset = 0, num_bytes = 0; @@ -5930,28 +7306,24 @@ static int check_csums(struct btrfs_root *root) return -ENOENT; } + btrfs_init_path(&path); key.objectid = BTRFS_EXTENT_CSUM_OBJECTID; key.type = BTRFS_EXTENT_CSUM_KEY; key.offset = 0; - - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - - ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); + ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0); if (ret < 0) { fprintf(stderr, "Error searching csum tree %d\n", ret); - btrfs_free_path(path); + btrfs_release_path(&path); return ret; } - if (ret > 0 && path->slots[0]) - path->slots[0]--; + if (ret > 0 && path.slots[0]) + path.slots[0]--; ret = 0; while (1) { - if (path->slots[0] >= btrfs_header_nritems(path->nodes[0])) { - ret = btrfs_next_leaf(root, path); + if (path.slots[0] >= btrfs_header_nritems(path.nodes[0])) { + ret = btrfs_next_leaf(root, &path); if (ret < 0) { fprintf(stderr, "Error going to next leaf " "%d\n", ret); @@ -5960,19 +7332,19 @@ static int check_csums(struct btrfs_root *root) if (ret) break; } - leaf = path->nodes[0]; + leaf = path.nodes[0]; - btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); + btrfs_item_key_to_cpu(leaf, &key, path.slots[0]); if (key.type != BTRFS_EXTENT_CSUM_KEY) { - path->slots[0]++; + path.slots[0]++; continue; } - data_len = (btrfs_item_size_nr(leaf, path->slots[0]) / + data_len = (btrfs_item_size_nr(leaf, path.slots[0]) / csum_size) * root->sectorsize; if (!check_data_csum) goto skip_csum_check; - leaf_offset = btrfs_item_ptr_offset(leaf, path->slots[0]); + leaf_offset = btrfs_item_ptr_offset(leaf, path.slots[0]); ret = check_extent_csums(root, key.offset, data_len, leaf_offset, leaf); if (ret) @@ -5992,10 +7364,10 @@ skip_csum_check: num_bytes = 0; } num_bytes += data_len; - path->slots[0]++; + path.slots[0]++; } - btrfs_free_path(path); + btrfs_release_path(&path); return errors; } @@ -6047,7 +7419,9 @@ static int calc_extent_flag(struct btrfs_root *root, cache = lookup_cache_extent(extent_cache, buf->start, 1); /* we have added this extent before */ - BUG_ON(!cache); + if (!cache) + return -ENOENT; + rec = container_of(cache, struct extent_record, cache); /* @@ -6682,12 +8056,11 @@ static int record_extent(struct btrfs_trans_handle *trans, struct extent_backref *back, int allocated, u64 flags) { - int ret; + int ret = 0; struct btrfs_root *extent_root = info->extent_root; struct extent_buffer *leaf; struct btrfs_key ins_key; struct btrfs_extent_item *ei; - struct tree_backref *tback; struct data_backref *dback; struct btrfs_tree_block_info *bi; @@ -6723,7 +8096,6 @@ static int record_extent(struct btrfs_trans_handle *trans, } else { struct btrfs_disk_key copy_key;; - tback = to_tree_backref(back); bi = (struct btrfs_tree_block_info *)(ei + 1); memset_extent_buffer(leaf, 0, (unsigned long)bi, sizeof(*bi)); @@ -6789,6 +8161,7 @@ static int record_extent(struct btrfs_trans_handle *trans, dback->found_ref); } else { u64 parent; + struct tree_backref *tback; tback = to_tree_backref(back); if (back->full_backref) @@ -6826,11 +8199,6 @@ static struct extent_entry *find_most_right_entry(struct list_head *entries) struct extent_entry *entry, *best = NULL, *prev = NULL; list_for_each_entry(entry, entries, list) { - if (!prev) { - prev = entry; - continue; - } - /* * If there are as many broken entries as entries then we know * not to trust this particular entry. @@ -6839,6 +8207,16 @@ static struct extent_entry *find_most_right_entry(struct list_head *entries) continue; /* + * Special case, when there are only two entries and 'best' is + * the first one + */ + if (!prev) { + best = entry; + prev = entry; + continue; + } + + /* * If our current entry == best then we can't be sure our best * is really the best, so we need to keep searching. */ @@ -7284,17 +8662,13 @@ static int delete_duplicate_records(struct btrfs_root *root, { struct btrfs_trans_handle *trans; LIST_HEAD(delete_list); - struct btrfs_path *path; + struct btrfs_path path; struct extent_record *tmp, *good, *n; int nr_del = 0; int ret = 0, err; struct btrfs_key key; - path = btrfs_alloc_path(); - if (!path) { - ret = -ENOMEM; - goto out; - } + btrfs_init_path(&path); good = rec; /* Find the record that covers all of the duplicates. */ @@ -7346,16 +8720,16 @@ static int delete_duplicate_records(struct btrfs_root *root, abort(); } - ret = btrfs_search_slot(trans, root, &key, path, -1, 1); + ret = btrfs_search_slot(trans, root, &key, &path, -1, 1); if (ret) { if (ret > 0) ret = -EINVAL; break; } - ret = btrfs_del_item(trans, root, path); + ret = btrfs_del_item(trans, root, &path); if (ret) break; - btrfs_release_path(path); + btrfs_release_path(&path); nr_del++; } err = btrfs_commit_transaction(trans, root); @@ -7376,7 +8750,7 @@ out: free(tmp); } - btrfs_free_path(path); + btrfs_release_path(&path); if (!ret && !nr_del) rec->num_duplicates = 0; @@ -7489,15 +8863,13 @@ static int record_orphan_data_extents(struct btrfs_fs_info *fs_info, struct extent_backref *back; struct data_backref *dback; struct orphan_data_extent *orphan; - struct btrfs_path *path; + struct btrfs_path path; int recorded_data_ref = 0; int ret = 0; if (rec->metadata) return 1; - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; + btrfs_init_path(&path); list_for_each_entry(back, &rec->backrefs, list) { if (back->full_backref || !back->is_data || !back->found_extent_tree) @@ -7519,7 +8891,8 @@ static int record_orphan_data_extents(struct btrfs_fs_info *fs_info, key.type = BTRFS_EXTENT_DATA_KEY; key.offset = dback->offset; - ret = btrfs_search_slot(NULL, dest_root, &key, path, 0, 0); + ret = btrfs_search_slot(NULL, dest_root, &key, &path, 0, 0); + btrfs_release_path(&path); /* * For ret < 0, it's OK since the fs-tree may be corrupted, * we need to record it for inode/file extent rebuild. @@ -7546,7 +8919,7 @@ static int record_orphan_data_extents(struct btrfs_fs_info *fs_info, recorded_data_ref = 1; } out: - btrfs_free_path(path); + btrfs_release_path(&path); if (!ret) return !recorded_data_ref; else @@ -7564,7 +8937,7 @@ static int fixup_extent_refs(struct btrfs_fs_info *info, { struct btrfs_trans_handle *trans = NULL; int ret; - struct btrfs_path *path; + struct btrfs_path path; struct list_head *cur = rec->backrefs.next; struct cache_extent *cache; struct extent_backref *back; @@ -7574,10 +8947,7 @@ static int fixup_extent_refs(struct btrfs_fs_info *info, if (rec->flag_block_full_backref) flags |= BTRFS_BLOCK_FLAG_FULL_BACKREF; - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - + btrfs_init_path(&path); if (rec->refs != rec->extent_item_refs && !rec->metadata) { /* * Sometimes the backrefs themselves are so broken they don't @@ -7586,13 +8956,13 @@ static int fixup_extent_refs(struct btrfs_fs_info *info, * them into the list if we find the backref so that * verify_backrefs can figure out what to do. */ - ret = find_possible_backrefs(info, path, extent_cache, rec); + ret = find_possible_backrefs(info, &path, extent_cache, rec); if (ret < 0) goto out; } /* step one, make sure all of the backrefs agree */ - ret = verify_backrefs(info, path, rec); + ret = verify_backrefs(info, &path, rec); if (ret < 0) goto out; @@ -7603,7 +8973,7 @@ static int fixup_extent_refs(struct btrfs_fs_info *info, } /* step two, delete all the existing records */ - ret = delete_extent_records(trans, info->extent_root, path, + ret = delete_extent_records(trans, info->extent_root, &path, rec->start, rec->max_size); if (ret < 0) @@ -7630,7 +9000,7 @@ static int fixup_extent_refs(struct btrfs_fs_info *info, continue; rec->bad_full_backref = 0; - ret = record_extent(trans, info, path, rec, back, allocated, flags); + ret = record_extent(trans, info, &path, rec, back, allocated, flags); allocated = 1; if (ret) @@ -7643,7 +9013,11 @@ out: ret = err; } - btrfs_free_path(path); + if (!ret) + fprintf(stderr, "Repaired extent references for %llu\n", + (unsigned long long)rec->start); + + btrfs_release_path(&path); return ret; } @@ -7652,7 +9026,7 @@ static int fixup_extent_flags(struct btrfs_fs_info *fs_info, { struct btrfs_trans_handle *trans; struct btrfs_root *root = fs_info->extent_root; - struct btrfs_path *path; + struct btrfs_path path; struct btrfs_extent_item *ei; struct btrfs_key key; u64 flags; @@ -7667,32 +9041,27 @@ static int fixup_extent_flags(struct btrfs_fs_info *fs_info, key.offset = rec->max_size; } - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - trans = btrfs_start_transaction(root, 0); - if (IS_ERR(trans)) { - btrfs_free_path(path); + if (IS_ERR(trans)) return PTR_ERR(trans); - } - ret = btrfs_search_slot(trans, root, &key, path, 0, 1); + btrfs_init_path(&path); + ret = btrfs_search_slot(trans, root, &key, &path, 0, 1); if (ret < 0) { - btrfs_free_path(path); + btrfs_release_path(&path); btrfs_commit_transaction(trans, root); return ret; } else if (ret) { fprintf(stderr, "Didn't find extent for %llu\n", (unsigned long long)rec->start); - btrfs_free_path(path); + btrfs_release_path(&path); btrfs_commit_transaction(trans, root); return -ENOENT; } - ei = btrfs_item_ptr(path->nodes[0], path->slots[0], + ei = btrfs_item_ptr(path.nodes[0], path.slots[0], struct btrfs_extent_item); - flags = btrfs_extent_flags(path->nodes[0], ei); + flags = btrfs_extent_flags(path.nodes[0], ei); if (rec->flag_block_full_backref) { fprintf(stderr, "setting full backref on %llu\n", (unsigned long long)key.objectid); @@ -7702,10 +9071,15 @@ static int fixup_extent_flags(struct btrfs_fs_info *fs_info, (unsigned long long)key.objectid); flags &= ~BTRFS_BLOCK_FLAG_FULL_BACKREF; } - btrfs_set_extent_flags(path->nodes[0], ei, flags); - btrfs_mark_buffer_dirty(path->nodes[0]); - btrfs_free_path(path); - return btrfs_commit_transaction(trans, root); + btrfs_set_extent_flags(path.nodes[0], ei, flags); + btrfs_mark_buffer_dirty(path.nodes[0]); + btrfs_release_path(&path); + ret = btrfs_commit_transaction(trans, root); + if (!ret) + fprintf(stderr, "Repaired extent flags for %llu\n", + (unsigned long long)rec->start); + + return ret; } /* right now we only prune from the extent allocation tree */ @@ -7832,11 +9206,8 @@ static int check_extent_refs(struct btrfs_root *root, { struct extent_record *rec; struct cache_extent *cache; - int err = 0; int ret = 0; - int fixed = 0; int had_dups = 0; - int recorded = 0; if (repair) { /* @@ -7905,9 +9276,8 @@ static int check_extent_refs(struct btrfs_root *root, while(1) { int cur_err = 0; + int fix = 0; - fixed = 0; - recorded = 0; cache = search_cache_extent(extent_cache, 0); if (!cache) break; @@ -7915,7 +9285,6 @@ static int check_extent_refs(struct btrfs_root *root, if (rec->num_duplicates) { fprintf(stderr, "extent item %llu has multiple extent " "items\n", (unsigned long long)rec->start); - err = 1; cur_err = 1; } @@ -7929,54 +9298,31 @@ static int check_extent_refs(struct btrfs_root *root, ret = record_orphan_data_extents(root->fs_info, rec); if (ret < 0) goto repair_abort; - if (ret == 0) { - recorded = 1; - } else { - /* - * we can't use the extent to repair file - * extent, let the fallback method handle it. - */ - if (!fixed && repair) { - ret = fixup_extent_refs( - root->fs_info, - extent_cache, rec); - if (ret) - goto repair_abort; - fixed = 1; - } - } - err = 1; + fix = ret; cur_err = 1; } if (all_backpointers_checked(rec, 1)) { fprintf(stderr, "backpointer mismatch on [%llu %llu]\n", (unsigned long long)rec->start, (unsigned long long)rec->nr); - - if (!fixed && !recorded && repair) { - ret = fixup_extent_refs(root->fs_info, - extent_cache, rec); - if (ret) - goto repair_abort; - fixed = 1; - } + fix = 1; cur_err = 1; - err = 1; } if (!rec->owner_ref_checked) { fprintf(stderr, "owner ref check failed [%llu %llu]\n", (unsigned long long)rec->start, (unsigned long long)rec->nr); - if (!fixed && !recorded && repair) { - ret = fixup_extent_refs(root->fs_info, - extent_cache, rec); - if (ret) - goto repair_abort; - fixed = 1; - } - err = 1; + fix = 1; cur_err = 1; } + + if (repair && fix) { + ret = fixup_extent_refs(root->fs_info, extent_cache, rec); + if (ret) + goto repair_abort; + } + + if (rec->bad_full_backref) { fprintf(stderr, "bad full backref, on [%llu]\n", (unsigned long long)rec->start); @@ -7984,9 +9330,8 @@ static int check_extent_refs(struct btrfs_root *root, ret = fixup_extent_flags(root->fs_info, rec); if (ret) goto repair_abort; - fixed = 1; + fix = 1; } - err = 1; cur_err = 1; } /* @@ -7998,7 +9343,6 @@ static int check_extent_refs(struct btrfs_root *root, fprintf(stderr, "bad metadata [%llu, %llu) crossing stripe boundary\n", rec->start, rec->start + rec->max_size); - err = 1; cur_err = 1; } @@ -8006,13 +9350,12 @@ static int check_extent_refs(struct btrfs_root *root, fprintf(stderr, "bad extent [%llu, %llu), type mismatch with chunk\n", rec->start, rec->start + rec->max_size); - err = 1; cur_err = 1; } remove_cache_extent(extent_cache, cache); free_all_extent_backrefs(rec); - if (!init_extent_tree && repair && (!cur_err || fixed)) + if (!init_extent_tree && repair && (!cur_err || fix)) clear_extent_dirty(root->fs_info->excluded_extents, rec->start, rec->start + rec->max_size - 1, @@ -8039,11 +9382,9 @@ repair_abort: if (ret) goto repair_abort; } - if (err) - fprintf(stderr, "repaired damaged extent references\n"); return ret; } - return err; + return 0; } u64 calc_stripe_length(u64 type, u64 length, int num_stripes) @@ -8486,7 +9827,7 @@ again: btrfs_init_path(&path); key.offset = 0; key.objectid = 0; - btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY); + key.type = BTRFS_ROOT_ITEM_KEY; ret = btrfs_search_slot(NULL, root->fs_info->tree_root, &key, &path, 0, 0); if (ret < 0) @@ -8502,7 +9843,7 @@ again: slot = path.slots[0]; } btrfs_item_key_to_cpu(leaf, &found_key, path.slots[0]); - if (btrfs_key_type(&found_key) == BTRFS_ROOT_ITEM_KEY) { + if (found_key.type == BTRFS_ROOT_ITEM_KEY) { unsigned long offset; u64 last_snapshot; @@ -8649,14 +9990,18 @@ static int check_tree_block_ref(struct btrfs_root *root, u32 nodesize = root->nodesize; u32 item_size; u64 offset; + int tree_reloc_root = 0; int found_ref = 0; int err = 0; int ret; + if (root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID && + btrfs_header_bytenr(root->node) == bytenr) + tree_reloc_root = 1; + btrfs_init_path(&path); key.objectid = bytenr; - if (btrfs_fs_incompat(root->fs_info, - BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA)) + if (btrfs_fs_incompat(root->fs_info, SKINNY_METADATA)) key.type = BTRFS_METADATA_ITEM_KEY; else key.type = BTRFS_EXTENT_ITEM_KEY; @@ -8741,9 +10086,16 @@ static int check_tree_block_ref(struct btrfs_root *root, (offset == root->objectid || offset == owner)) { found_ref = 1; } else if (type == BTRFS_SHARED_BLOCK_REF_KEY) { + /* + * Backref of tree reloc root points to itself, no need + * to check backref any more. + */ + if (tree_reloc_root) + found_ref = 1; + else /* Check if the backref points to valid referencer */ - found_ref = !check_tree_block_ref(root, NULL, offset, - level + 1, owner); + found_ref = !check_tree_block_ref(root, NULL, + offset, level + 1, owner); } if (found_ref) @@ -8794,12 +10146,10 @@ static int check_extent_data_item(struct btrfs_root *root, struct btrfs_extent_inline_ref *iref; struct btrfs_extent_data_ref *dref; u64 owner; - u64 file_extent_gen; u64 disk_bytenr; u64 disk_num_bytes; u64 extent_num_bytes; u64 extent_flags; - u64 extent_gen; u32 item_size; unsigned long end; unsigned long ptr; @@ -8811,7 +10161,6 @@ static int check_extent_data_item(struct btrfs_root *root, btrfs_item_key_to_cpu(eb, &fi_key, slot); fi = btrfs_item_ptr(eb, slot, struct btrfs_file_extent_item); - file_extent_gen = btrfs_file_extent_generation(eb, fi); /* Nothing to check for hole and inline data extents */ if (btrfs_file_extent_type(eb, fi) == BTRFS_FILE_EXTENT_INLINE || @@ -8860,7 +10209,6 @@ static int check_extent_data_item(struct btrfs_root *root, ei = btrfs_item_ptr(leaf, slot, struct btrfs_extent_item); extent_flags = btrfs_extent_flags(leaf, ei); - extent_gen = btrfs_extent_generation(leaf, ei); if (!(extent_flags & BTRFS_EXTENT_FLAG_DATA)) { error( @@ -8870,14 +10218,6 @@ static int check_extent_data_item(struct btrfs_root *root, err |= BACKREF_MISMATCH; } - if (file_extent_gen < extent_gen) { - error( -"extent[%llu %llu] backref generation mismatch, wanted: <=%llu, have: %llu", - disk_bytenr, disk_num_bytes, file_extent_gen, - extent_gen); - err |= BACKREF_MISMATCH; - } - /* Check data backref inside that extent item */ item_size = btrfs_item_size_nr(leaf, path.slots[0]); iref = (struct btrfs_extent_inline_ref *)(ei + 1); @@ -9104,6 +10444,34 @@ out: } /* + * Check if tree block @eb is tree reloc root. + * Return 0 if it's not or any problem happens + * Return 1 if it's a tree reloc root + */ +static int is_tree_reloc_root(struct btrfs_fs_info *fs_info, + struct extent_buffer *eb) +{ + struct btrfs_root *tree_reloc_root; + struct btrfs_key key; + u64 bytenr = btrfs_header_bytenr(eb); + u64 owner = btrfs_header_owner(eb); + int ret = 0; + + key.objectid = BTRFS_TREE_RELOC_OBJECTID; + key.offset = owner; + key.type = BTRFS_ROOT_ITEM_KEY; + + tree_reloc_root = btrfs_read_fs_root_no_cache(fs_info, &key); + if (IS_ERR(tree_reloc_root)) + return 0; + + if (bytenr == btrfs_header_bytenr(tree_reloc_root->node)) + ret = 1; + btrfs_free_fs_root(tree_reloc_root); + return ret; +} + +/* * Check referencer for shared block backref * If level == -1, this function will resolve the level. */ @@ -9125,6 +10493,13 @@ static int check_shared_block_backref(struct btrfs_fs_info *fs_info, if (level < 0) goto out; + /* It's possible it's a tree reloc root */ + if (parent == bytenr) { + if (is_tree_reloc_root(fs_info, eb)) + found_parent = 1; + goto out; + } + if (level + 1 != btrfs_header_level(eb)) goto out; @@ -9184,7 +10559,7 @@ static int check_extent_data_backref(struct btrfs_fs_info *fs_info, btrfs_release_path(&path); } key.objectid = root_id; - btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY); + key.type = BTRFS_ROOT_ITEM_KEY; key.offset = (u64)-1; btrfs_init_path(&path); @@ -9336,7 +10711,8 @@ static int check_extent_item(struct btrfs_fs_info *fs_info, if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) metadata = 1; - if (metadata && check_crossing_stripes(key.objectid, eb->len)) { + if (metadata && check_crossing_stripes(global_info, key.objectid, + eb->len)) { error("bad metadata [%llu, %llu) crossing stripe boundary", key.objectid, key.objectid + nodesize); err |= CROSSING_STRIPE_BOUNDARY; @@ -9784,7 +11160,7 @@ static int check_leaf_items(struct btrfs_root *root, struct extent_buffer *eb) next: btrfs_item_key_to_cpu(eb, &key, slot); - type = btrfs_key_type(&key); + type = key.type; switch (type) { case BTRFS_EXTENT_DATA_KEY: @@ -10061,7 +11437,11 @@ static int check_chunks_and_extents_v2(struct btrfs_root *root) goto next; key.offset = (u64)-1; - cur_root = btrfs_read_fs_root(root->fs_info, &key); + if (key.objectid == BTRFS_TREE_RELOC_OBJECTID) + cur_root = btrfs_read_fs_root_no_cache(root->fs_info, + &key); + else + cur_root = btrfs_read_fs_root(root->fs_info, &key); if (IS_ERR(cur_root) || !cur_root) { error("failed to read tree: %lld", key.objectid); goto next; @@ -10070,6 +11450,8 @@ static int check_chunks_and_extents_v2(struct btrfs_root *root) ret = traverse_tree_block(cur_root, cur_root->node); err |= ret; + if (key.objectid == BTRFS_TREE_RELOC_OBJECTID) + btrfs_free_fs_root(cur_root); next: ret = btrfs_next_item(root1, &path); if (ret) @@ -10242,24 +11624,20 @@ static int pin_metadata_blocks(struct btrfs_fs_info *fs_info) static int reset_block_groups(struct btrfs_fs_info *fs_info) { struct btrfs_block_group_cache *cache; - struct btrfs_path *path; + struct btrfs_path path; struct extent_buffer *leaf; struct btrfs_chunk *chunk; struct btrfs_key key; int ret; u64 start; - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - + btrfs_init_path(&path); key.objectid = 0; key.type = BTRFS_CHUNK_ITEM_KEY; key.offset = 0; - - ret = btrfs_search_slot(NULL, fs_info->chunk_root, &key, path, 0, 0); + ret = btrfs_search_slot(NULL, fs_info->chunk_root, &key, &path, 0, 0); if (ret < 0) { - btrfs_free_path(path); + btrfs_release_path(&path); return ret; } @@ -10274,10 +11652,10 @@ static int reset_block_groups(struct btrfs_fs_info *fs_info) /* First we need to create the in-memory block groups */ while (1) { - if (path->slots[0] >= btrfs_header_nritems(path->nodes[0])) { - ret = btrfs_next_leaf(fs_info->chunk_root, path); + if (path.slots[0] >= btrfs_header_nritems(path.nodes[0])) { + ret = btrfs_next_leaf(fs_info->chunk_root, &path); if (ret < 0) { - btrfs_free_path(path); + btrfs_release_path(&path); return ret; } if (ret) { @@ -10285,15 +11663,14 @@ static int reset_block_groups(struct btrfs_fs_info *fs_info) break; } } - leaf = path->nodes[0]; - btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); + leaf = path.nodes[0]; + btrfs_item_key_to_cpu(leaf, &key, path.slots[0]); if (key.type != BTRFS_CHUNK_ITEM_KEY) { - path->slots[0]++; + path.slots[0]++; continue; } - chunk = btrfs_item_ptr(leaf, path->slots[0], - struct btrfs_chunk); + chunk = btrfs_item_ptr(leaf, path.slots[0], struct btrfs_chunk); btrfs_add_block_group(fs_info, 0, btrfs_chunk_type(leaf, chunk), key.objectid, key.offset, @@ -10301,7 +11678,7 @@ static int reset_block_groups(struct btrfs_fs_info *fs_info) set_extent_dirty(&fs_info->free_space_cache, key.offset, key.offset + btrfs_chunk_length(leaf, chunk), GFP_NOFS); - path->slots[0]++; + path.slots[0]++; } start = 0; while (1) { @@ -10312,7 +11689,7 @@ static int reset_block_groups(struct btrfs_fs_info *fs_info) start = cache->key.objectid + cache->key.offset; } - btrfs_free_path(path); + btrfs_release_path(&path); return 0; } @@ -10320,22 +11697,18 @@ static int reset_balance(struct btrfs_trans_handle *trans, struct btrfs_fs_info *fs_info) { struct btrfs_root *root = fs_info->tree_root; - struct btrfs_path *path; + struct btrfs_path path; struct extent_buffer *leaf; struct btrfs_key key; int del_slot, del_nr = 0; int ret; int found = 0; - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - + btrfs_init_path(&path); key.objectid = BTRFS_BALANCE_OBJECTID; key.type = BTRFS_BALANCE_ITEM_KEY; key.offset = 0; - - ret = btrfs_search_slot(trans, root, &key, path, -1, 1); + ret = btrfs_search_slot(trans, root, &key, &path, -1, 1); if (ret) { if (ret > 0) ret = 0; @@ -10345,64 +11718,63 @@ static int reset_balance(struct btrfs_trans_handle *trans, goto out; } - ret = btrfs_del_item(trans, root, path); + ret = btrfs_del_item(trans, root, &path); if (ret) goto out; - btrfs_release_path(path); + btrfs_release_path(&path); key.objectid = BTRFS_TREE_RELOC_OBJECTID; key.type = BTRFS_ROOT_ITEM_KEY; key.offset = 0; - - ret = btrfs_search_slot(trans, root, &key, path, -1, 1); + ret = btrfs_search_slot(trans, root, &key, &path, -1, 1); if (ret < 0) goto out; while (1) { - if (path->slots[0] >= btrfs_header_nritems(path->nodes[0])) { + if (path.slots[0] >= btrfs_header_nritems(path.nodes[0])) { if (!found) break; if (del_nr) { - ret = btrfs_del_items(trans, root, path, + ret = btrfs_del_items(trans, root, &path, del_slot, del_nr); del_nr = 0; if (ret) goto out; } key.offset++; - btrfs_release_path(path); + btrfs_release_path(&path); found = 0; - ret = btrfs_search_slot(trans, root, &key, path, + ret = btrfs_search_slot(trans, root, &key, &path, -1, 1); if (ret < 0) goto out; continue; } found = 1; - leaf = path->nodes[0]; - btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); + leaf = path.nodes[0]; + btrfs_item_key_to_cpu(leaf, &key, path.slots[0]); if (key.objectid > BTRFS_TREE_RELOC_OBJECTID) break; if (key.objectid != BTRFS_TREE_RELOC_OBJECTID) { - path->slots[0]++; + path.slots[0]++; continue; } if (!del_nr) { - del_slot = path->slots[0]; + del_slot = path.slots[0]; del_nr = 1; } else { del_nr++; } - path->slots[0]++; + path.slots[0]++; } if (del_nr) { - ret = btrfs_del_items(trans, root, path, del_slot, del_nr); + ret = btrfs_del_items(trans, root, &path, del_slot, del_nr); if (ret) goto out; } - btrfs_release_path(path); + btrfs_release_path(&path); reinit_data_reloc: key.objectid = BTRFS_DATA_RELOC_TREE_OBJECTID; @@ -10420,7 +11792,7 @@ reinit_data_reloc: goto out; ret = btrfs_make_root_dir(trans, root, BTRFS_FIRST_FREE_OBJECTID); out: - btrfs_free_path(path); + btrfs_release_path(&path); return ret; } @@ -10437,7 +11809,7 @@ static int reinit_extent_tree(struct btrfs_trans_handle *trans, * the leaves of any fs roots and pin down the bytes for any file * extents we find. Not hard but why do it if we don't have to? */ - if (btrfs_fs_incompat(fs_info, BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS)) { + if (btrfs_fs_incompat(fs_info, MIXED_GROUPS)) { fprintf(stderr, "We don't support re-initing the extent tree " "for mixed block groups yet, please notify a btrfs " "developer you want to do this so they can add this " @@ -10510,7 +11882,7 @@ static int reinit_extent_tree(struct btrfs_trans_handle *trans, static int recow_extent_buffer(struct btrfs_root *root, struct extent_buffer *eb) { - struct btrfs_path *path; + struct btrfs_path path; struct btrfs_trans_handle *trans; struct btrfs_key key; int ret; @@ -10527,31 +11899,26 @@ static int recow_extent_buffer(struct btrfs_root *root, struct extent_buffer *eb return PTR_ERR(root); } - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - trans = btrfs_start_transaction(root, 1); - if (IS_ERR(trans)) { - btrfs_free_path(path); + if (IS_ERR(trans)) return PTR_ERR(trans); - } - path->lowest_level = btrfs_header_level(eb); - if (path->lowest_level) + btrfs_init_path(&path); + path.lowest_level = btrfs_header_level(eb); + if (path.lowest_level) btrfs_node_key_to_cpu(eb, &key, 0); else btrfs_item_key_to_cpu(eb, &key, 0); - ret = btrfs_search_slot(trans, root, &key, path, 0, 1); + ret = btrfs_search_slot(trans, root, &key, &path, 0, 1); btrfs_commit_transaction(trans, root); - btrfs_free_path(path); + btrfs_release_path(&path); return ret; } static int delete_bad_item(struct btrfs_root *root, struct bad_item *bad) { - struct btrfs_path *path; + struct btrfs_path path; struct btrfs_trans_handle *trans; struct btrfs_key key; int ret; @@ -10569,26 +11936,21 @@ static int delete_bad_item(struct btrfs_root *root, struct bad_item *bad) return PTR_ERR(root); } - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - trans = btrfs_start_transaction(root, 1); - if (IS_ERR(trans)) { - btrfs_free_path(path); + if (IS_ERR(trans)) return PTR_ERR(trans); - } - ret = btrfs_search_slot(trans, root, &bad->key, path, -1, 1); + btrfs_init_path(&path); + ret = btrfs_search_slot(trans, root, &bad->key, &path, -1, 1); if (ret) { if (ret > 0) ret = 0; goto out; } - ret = btrfs_del_item(trans, root, path); + ret = btrfs_del_item(trans, root, &path); out: btrfs_commit_transaction(trans, root); - btrfs_free_path(path); + btrfs_release_path(&path); return ret; } @@ -10635,7 +11997,7 @@ static int fill_csum_tree_from_one_fs_root(struct btrfs_trans_handle *trans, struct btrfs_root *csum_root, struct btrfs_root *cur_root) { - struct btrfs_path *path; + struct btrfs_path path; struct btrfs_key key; struct extent_buffer *node; struct btrfs_file_extent_item *fi; @@ -10645,30 +12007,25 @@ static int fill_csum_tree_from_one_fs_root(struct btrfs_trans_handle *trans, int slot = 0; int ret = 0; - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; buf = malloc(cur_root->fs_info->csum_root->sectorsize); - if (!buf) { - ret = -ENOMEM; - goto out; - } + if (!buf) + return -ENOMEM; + btrfs_init_path(&path); key.objectid = 0; key.offset = 0; key.type = 0; - - ret = btrfs_search_slot(NULL, cur_root, &key, path, 0, 0); + ret = btrfs_search_slot(NULL, cur_root, &key, &path, 0, 0); if (ret < 0) goto out; /* Iterate all regular file extents and fill its csum */ while (1) { - btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); + btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]); if (key.type != BTRFS_EXTENT_DATA_KEY) goto next; - node = path->nodes[0]; - slot = path->slots[0]; + node = path.nodes[0]; + slot = path.slots[0]; fi = btrfs_item_ptr(node, slot, struct btrfs_file_extent_item); if (btrfs_file_extent_type(node, fi) != BTRFS_FILE_EXTENT_REG) goto next; @@ -10685,7 +12042,7 @@ next: * TODO: if next leaf is corrupted, jump to nearest next valid * leaf. */ - ret = btrfs_next_item(cur_root, path); + ret = btrfs_next_item(cur_root, &path); if (ret < 0) goto out; if (ret > 0) { @@ -10695,7 +12052,7 @@ next: } out: - btrfs_free_path(path); + btrfs_release_path(&path); free(buf); return ret; } @@ -10704,7 +12061,7 @@ static int fill_csum_tree_from_fs(struct btrfs_trans_handle *trans, struct btrfs_root *csum_root) { struct btrfs_fs_info *fs_info = csum_root->fs_info; - struct btrfs_path *path; + struct btrfs_path path; struct btrfs_root *tree_root = fs_info->tree_root; struct btrfs_root *cur_root; struct extent_buffer *node; @@ -10712,15 +12069,11 @@ static int fill_csum_tree_from_fs(struct btrfs_trans_handle *trans, int slot = 0; int ret = 0; - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - + btrfs_init_path(&path); key.objectid = BTRFS_FS_TREE_OBJECTID; key.offset = 0; key.type = BTRFS_ROOT_ITEM_KEY; - - ret = btrfs_search_slot(NULL, tree_root, &key, path, 0, 0); + ret = btrfs_search_slot(NULL, tree_root, &key, &path, 0, 0); if (ret < 0) goto out; if (ret > 0) { @@ -10729,8 +12082,8 @@ static int fill_csum_tree_from_fs(struct btrfs_trans_handle *trans, } while (1) { - node = path->nodes[0]; - slot = path->slots[0]; + node = path.nodes[0]; + slot = path.slots[0]; btrfs_item_key_to_cpu(node, &key, slot); if (key.objectid > BTRFS_LAST_FREE_OBJECTID) goto out; @@ -10751,7 +12104,7 @@ static int fill_csum_tree_from_fs(struct btrfs_trans_handle *trans, if (ret < 0) goto out; next: - ret = btrfs_next_item(tree_root, path); + ret = btrfs_next_item(tree_root, &path); if (ret > 0) { ret = 0; goto out; @@ -10761,7 +12114,7 @@ next: } out: - btrfs_free_path(path); + btrfs_release_path(&path); return ret; } @@ -10769,36 +12122,32 @@ static int fill_csum_tree_from_extent(struct btrfs_trans_handle *trans, struct btrfs_root *csum_root) { struct btrfs_root *extent_root = csum_root->fs_info->extent_root; - struct btrfs_path *path; + struct btrfs_path path; struct btrfs_extent_item *ei; struct extent_buffer *leaf; char *buf; struct btrfs_key key; int ret; - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - + btrfs_init_path(&path); key.objectid = 0; key.type = BTRFS_EXTENT_ITEM_KEY; key.offset = 0; - - ret = btrfs_search_slot(NULL, extent_root, &key, path, 0, 0); + ret = btrfs_search_slot(NULL, extent_root, &key, &path, 0, 0); if (ret < 0) { - btrfs_free_path(path); + btrfs_release_path(&path); return ret; } buf = malloc(csum_root->sectorsize); if (!buf) { - btrfs_free_path(path); + btrfs_release_path(&path); return -ENOMEM; } while (1) { - if (path->slots[0] >= btrfs_header_nritems(path->nodes[0])) { - ret = btrfs_next_leaf(extent_root, path); + if (path.slots[0] >= btrfs_header_nritems(path.nodes[0])) { + ret = btrfs_next_leaf(extent_root, &path); if (ret < 0) break; if (ret) { @@ -10806,19 +12155,19 @@ static int fill_csum_tree_from_extent(struct btrfs_trans_handle *trans, break; } } - leaf = path->nodes[0]; + leaf = path.nodes[0]; - btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); + btrfs_item_key_to_cpu(leaf, &key, path.slots[0]); if (key.type != BTRFS_EXTENT_ITEM_KEY) { - path->slots[0]++; + path.slots[0]++; continue; } - ei = btrfs_item_ptr(leaf, path->slots[0], + ei = btrfs_item_ptr(leaf, path.slots[0], struct btrfs_extent_item); if (!(btrfs_extent_flags(leaf, ei) & BTRFS_EXTENT_FLAG_DATA)) { - path->slots[0]++; + path.slots[0]++; continue; } @@ -10826,10 +12175,10 @@ static int fill_csum_tree_from_extent(struct btrfs_trans_handle *trans, key.offset); if (ret) break; - path->slots[0]++; + path.slots[0]++; } - btrfs_free_path(path); + btrfs_release_path(&path); free(buf); return ret; } @@ -10877,7 +12226,7 @@ static int build_roots_info_cache(struct btrfs_fs_info *info) int ret = 0; struct btrfs_key key; struct extent_buffer *leaf; - struct btrfs_path *path; + struct btrfs_path path; if (!roots_info_cache) { roots_info_cache = malloc(sizeof(*roots_info_cache)); @@ -10886,24 +12235,20 @@ static int build_roots_info_cache(struct btrfs_fs_info *info) cache_tree_init(roots_info_cache); } - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - + btrfs_init_path(&path); key.objectid = 0; key.type = BTRFS_EXTENT_ITEM_KEY; key.offset = 0; - - ret = btrfs_search_slot(NULL, info->extent_root, &key, path, 0, 0); + ret = btrfs_search_slot(NULL, info->extent_root, &key, &path, 0, 0); if (ret < 0) goto out; - leaf = path->nodes[0]; + leaf = path.nodes[0]; while (1) { struct btrfs_key found_key; struct btrfs_extent_item *ei; struct btrfs_extent_inline_ref *iref; - int slot = path->slots[0]; + int slot = path.slots[0]; int type; u64 flags; u64 root_id; @@ -10912,18 +12257,18 @@ static int build_roots_info_cache(struct btrfs_fs_info *info) struct root_item_info *rii; if (slot >= btrfs_header_nritems(leaf)) { - ret = btrfs_next_leaf(info->extent_root, path); + ret = btrfs_next_leaf(info->extent_root, &path); if (ret < 0) { break; } else if (ret) { ret = 0; break; } - leaf = path->nodes[0]; - slot = path->slots[0]; + leaf = path.nodes[0]; + slot = path.slots[0]; } - btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]); + btrfs_item_key_to_cpu(leaf, &found_key, path.slots[0]); if (found_key.type != BTRFS_EXTENT_ITEM_KEY && found_key.type != BTRFS_METADATA_ITEM_KEY) @@ -10986,11 +12331,11 @@ static int build_roots_info_cache(struct btrfs_fs_info *info) rii->node_count++; } next: - path->slots[0]++; + path.slots[0]++; } out: - btrfs_free_path(path); + btrfs_release_path(&path); return ret; } @@ -11086,7 +12431,7 @@ static int maybe_repair_root_item(struct btrfs_fs_info *info, */ static int repair_root_items(struct btrfs_fs_info *info) { - struct btrfs_path *path = NULL; + struct btrfs_path path; struct btrfs_key key; struct extent_buffer *leaf; struct btrfs_trans_handle *trans = NULL; @@ -11094,16 +12439,12 @@ static int repair_root_items(struct btrfs_fs_info *info) int bad_roots = 0; int need_trans = 0; + btrfs_init_path(&path); + ret = build_roots_info_cache(info); if (ret) goto out; - path = btrfs_alloc_path(); - if (!path) { - ret = -ENOMEM; - goto out; - } - key.objectid = BTRFS_FIRST_FREE_OBJECTID; key.type = BTRFS_ROOT_ITEM_KEY; key.offset = 0; @@ -11122,19 +12463,19 @@ again: } } - ret = btrfs_search_slot(trans, info->tree_root, &key, path, + ret = btrfs_search_slot(trans, info->tree_root, &key, &path, 0, trans ? 1 : 0); if (ret < 0) goto out; - leaf = path->nodes[0]; + leaf = path.nodes[0]; while (1) { struct btrfs_key found_key; - if (path->slots[0] >= btrfs_header_nritems(leaf)) { - int no_more_keys = find_next_key(path, &key); + if (path.slots[0] >= btrfs_header_nritems(leaf)) { + int no_more_keys = find_next_key(&path, &key); - btrfs_release_path(path); + btrfs_release_path(&path); if (trans) { ret = btrfs_commit_transaction(trans, info->tree_root); @@ -11148,14 +12489,14 @@ again: goto again; } - btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]); + btrfs_item_key_to_cpu(leaf, &found_key, path.slots[0]); if (found_key.type != BTRFS_ROOT_ITEM_KEY) goto next; if (found_key.objectid == BTRFS_TREE_RELOC_OBJECTID) goto next; - ret = maybe_repair_root_item(info, path, &found_key, + ret = maybe_repair_root_item(info, &path, &found_key, trans ? 0 : 1); if (ret < 0) goto out; @@ -11163,18 +12504,18 @@ again: if (!trans && repair) { need_trans = 1; key = found_key; - btrfs_release_path(path); + btrfs_release_path(&path); goto again; } bad_roots++; } next: - path->slots[0]++; + path.slots[0]++; } ret = 0; out: free_roots_info_cache(); - btrfs_free_path(path); + btrfs_release_path(&path); if (trans) btrfs_commit_transaction(trans, info->tree_root); if (ret < 0) @@ -11183,6 +12524,36 @@ out: return bad_roots; } +static int clear_free_space_cache(struct btrfs_fs_info *fs_info) +{ + struct btrfs_trans_handle *trans; + struct btrfs_block_group_cache *bg_cache; + u64 current = 0; + int ret = 0; + + /* Clear all free space cache inodes and its extent data */ + while (1) { + bg_cache = btrfs_lookup_first_block_group(fs_info, current); + if (!bg_cache) + break; + ret = btrfs_clear_free_space_cache(fs_info, bg_cache); + if (ret < 0) + return ret; + current = bg_cache->key.objectid + bg_cache->key.offset; + } + + /* Don't forget to set cache_generation to -1 */ + trans = btrfs_start_transaction(fs_info->tree_root, 0); + if (IS_ERR(trans)) { + error("failed to update super block cache generation"); + return PTR_ERR(trans); + } + btrfs_set_super_cache_generation(fs_info->super_copy, (u64)-1); + btrfs_commit_transaction(trans, fs_info->tree_root); + + return ret; +} + const char * const cmd_check_usage[] = { "btrfs check [options] <device>", "Check structural integrity of a filesystem (unmounted).", @@ -11197,19 +12568,20 @@ const char * const cmd_check_usage[] = { "--readonly run in read-only mode (default)", "--init-csum-tree create a new CRC tree", "--init-extent-tree create a new extent tree", - "--mode <MODE> select mode, allows to make some memory/IO", - " trade-offs, where MODE is one of:", + "--mode <MODE> allows choice of memory/IO trade-offs", + " where MODE is one of:", " original - read inodes and extents to memory (requires", " more memory, does less IO)", " lowmem - try to use less memory but read blocks again", " when needed", "--check-data-csum verify checksums of data blocks", - "-Q|--qgroup-report print a report on qgroup consistency", + "-Q|--qgroup-report print a report on qgroup consistency", "-E|--subvol-extents <subvolid>", " print subvolume extents and sharing state", "-r|--tree-root <bytenr> use the given bytenr for the tree root", "--chunk-root <bytenr> use the given bytenr for the chunk tree root", "-p|--progress indicate progress", + "--clear-space-cache v1|v2 clear space cache for v1 or v2", NULL }; @@ -11224,9 +12596,11 @@ int cmd_check(int argc, char **argv) u64 chunk_root_bytenr = 0; char uuidbuf[BTRFS_UUID_UNPARSED_SIZE]; int ret; + int err = 0; u64 num; int init_csum_tree = 0; int readonly = 0; + int clear_space_cache = 0; int qgroup_report = 0; int qgroups_repaired = 0; unsigned ctree_flags = OPEN_CTREE_EXCLUSIVE; @@ -11236,7 +12610,7 @@ int cmd_check(int argc, char **argv) enum { GETOPT_VAL_REPAIR = 257, GETOPT_VAL_INIT_CSUM, GETOPT_VAL_INIT_EXTENT, GETOPT_VAL_CHECK_CSUM, GETOPT_VAL_READONLY, GETOPT_VAL_CHUNK_TREE, - GETOPT_VAL_MODE }; + GETOPT_VAL_MODE, GETOPT_VAL_CLEAR_SPACE_CACHE }; static const struct option long_options[] = { { "super", required_argument, NULL, 's' }, { "repair", no_argument, NULL, GETOPT_VAL_REPAIR }, @@ -11256,6 +12630,8 @@ int cmd_check(int argc, char **argv) { "progress", no_argument, NULL, 'p' }, { "mode", required_argument, NULL, GETOPT_VAL_MODE }, + { "clear-space-cache", required_argument, NULL, + GETOPT_VAL_CLEAR_SPACE_CACHE}, { NULL, 0, NULL, 0} }; @@ -11270,8 +12646,8 @@ int cmd_check(int argc, char **argv) case 's': num = arg_strtou64(optarg); if (num >= BTRFS_SUPER_MIRROR_MAX) { - fprintf(stderr, - "ERROR: super mirror should be less than: %d\n", + error( + "super mirror should be less than %d", BTRFS_SUPER_MIRROR_MAX); exit(1); } @@ -11327,6 +12703,19 @@ int cmd_check(int argc, char **argv) exit(1); } break; + case GETOPT_VAL_CLEAR_SPACE_CACHE: + if (strcmp(optarg, "v1") == 0) { + clear_space_cache = 1; + } else if (strcmp(optarg, "v2") == 0) { + clear_space_cache = 2; + ctree_flags |= OPEN_CTREE_INVALIDATE_FST; + } else { + error( + "invalid argument to --clear-space-cache, must be v1 or v2"); + exit(1); + } + ctree_flags |= OPEN_CTREE_WRITES; + break; } } @@ -11340,7 +12729,7 @@ int cmd_check(int argc, char **argv) /* This check is the only reason for --readonly to exist */ if (readonly && repair) { - fprintf(stderr, "Repair options are not compatible with --readonly\n"); + error("repair options are not compatible with --readonly"); exit(1); } @@ -11348,7 +12737,7 @@ int cmd_check(int argc, char **argv) * Not supported yet */ if (repair && check_mode == CHECK_MODE_LOWMEM) { - error("Low memory mode doesn't support repair yet"); + error("low memory mode doesn't support repair yet"); exit(1); } @@ -11356,11 +12745,13 @@ int cmd_check(int argc, char **argv) cache_tree_init(&root_cache); if((ret = check_mounted(argv[optind])) < 0) { - fprintf(stderr, "Could not check mount status: %s\n", strerror(-ret)); + error("could not check mount status: %s", strerror(-ret)); + err |= !!ret; goto err_out; } else if(ret) { - fprintf(stderr, "%s is currently mounted. Aborting.\n", argv[optind]); + error("%s is currently mounted, aborting", argv[optind]); ret = -EBUSY; + err |= !!ret; goto err_out; } @@ -11371,27 +12762,62 @@ int cmd_check(int argc, char **argv) info = open_ctree_fs_info(argv[optind], bytenr, tree_root_bytenr, chunk_root_bytenr, ctree_flags); if (!info) { - fprintf(stderr, "Couldn't open file system\n"); + error("cannot open file system"); ret = -EIO; + err |= !!ret; goto err_out; } global_info = info; root = info->fs_root; + if (clear_space_cache == 1) { + if (btrfs_fs_compat_ro(info, FREE_SPACE_TREE)) { + error( + "free space cache v2 detected, use --clear-space-cache v2"); + ret = 1; + goto close_out; + } + printf("Clearing free space cache\n"); + ret = clear_free_space_cache(info); + if (ret) { + error("failed to clear free space cache"); + ret = 1; + } else { + printf("Free space cache cleared\n"); + } + goto close_out; + } else if (clear_space_cache == 2) { + if (!btrfs_fs_compat_ro(info, FREE_SPACE_TREE)) { + printf("no free space cache v2 to clear\n"); + ret = 0; + goto close_out; + } + printf("Clear free space cache v2\n"); + ret = btrfs_clear_free_space_tree(info); + if (ret) { + error("failed to clear free space cache v2: %d", ret); + ret = 1; + } else { + printf("free space cache v2 cleared\n"); + } + goto close_out; + } /* * repair mode will force us to commit transaction which * will make us fail to load log tree when mounting. */ if (repair && btrfs_super_log_root(info->super_copy)) { - ret = ask_user("repair mode will force to clear out log tree, Are you sure?"); + ret = ask_user("repair mode will force to clear out log tree, are you sure?"); if (!ret) { ret = 1; + err |= !!ret; goto close_out; } ret = zero_log_tree(root); + err |= !!ret; if (ret) { - fprintf(stderr, "fail to zero log tree\n"); + error("failed to zero log tree: %d", ret); goto close_out; } } @@ -11401,6 +12827,7 @@ int cmd_check(int argc, char **argv) printf("Print quota groups for %s\nUUID: %s\n", argv[optind], uuidbuf); ret = qgroup_verify_all(info); + err |= !!ret; if (ret == 0) report_qgroups(1); goto close_out; @@ -11409,6 +12836,7 @@ int cmd_check(int argc, char **argv) printf("Print extent state for subvolume %llu on %s\nUUID: %s\n", subvolid, argv[optind], uuidbuf); ret = print_extent_state(info, subvolid); + err |= !!ret; goto close_out; } printf("Checking filesystem on %s\nUUID: %s\n", argv[optind], uuidbuf); @@ -11416,7 +12844,8 @@ int cmd_check(int argc, char **argv) if (!extent_buffer_uptodate(info->tree_root->node) || !extent_buffer_uptodate(info->dev_root->node) || !extent_buffer_uptodate(info->chunk_root->node)) { - fprintf(stderr, "Critical roots corrupted, unable to fsck the FS\n"); + error("critical roots corrupted, unable to check the filesystem"); + err |= !!ret; ret = -EIO; goto close_out; } @@ -11426,31 +12855,36 @@ int cmd_check(int argc, char **argv) trans = btrfs_start_transaction(info->extent_root, 0); if (IS_ERR(trans)) { - fprintf(stderr, "Error starting transaction\n"); + error("error starting transaction"); ret = PTR_ERR(trans); + err |= !!ret; goto close_out; } if (init_extent_tree) { printf("Creating a new extent tree\n"); ret = reinit_extent_tree(trans, info); + err |= !!ret; if (ret) goto close_out; } if (init_csum_tree) { - fprintf(stderr, "Reinit crc root\n"); + printf("Reinitialize checksum tree\n"); ret = btrfs_fsck_reinit_root(trans, info->csum_root, 0); if (ret) { - fprintf(stderr, "crc root initialization failed\n"); + error("checksum tree initialization failed: %d", + ret); ret = -EIO; + err |= !!ret; goto close_out; } ret = fill_csum_tree(trans, info->csum_root, init_extent_tree); + err |= !!ret; if (ret) { - fprintf(stderr, "crc refilling failed\n"); + error("checksum tree refilling failed: %d", ret); return -EIO; } } @@ -11459,17 +12893,20 @@ int cmd_check(int argc, char **argv) * extent entries for all of the items it finds. */ ret = btrfs_commit_transaction(trans, info->extent_root); + err |= !!ret; if (ret) goto close_out; } if (!extent_buffer_uptodate(info->extent_root->node)) { - fprintf(stderr, "Critical roots corrupted, unable to fsck the FS\n"); + error("critical: extent_root, unable to check the filesystem"); ret = -EIO; + err |= !!ret; goto close_out; } if (!extent_buffer_uptodate(info->csum_root->node)) { - fprintf(stderr, "Checksum root corrupted, rerun with --init-csum-tree option\n"); + error("critical: csum_root, unable to check the filesystem"); ret = -EIO; + err |= !!ret; goto close_out; } @@ -11479,10 +12916,13 @@ int cmd_check(int argc, char **argv) ret = check_chunks_and_extents_v2(root); else ret = check_chunks_and_extents(root); + err |= !!ret; if (ret) - fprintf(stderr, "Errors found in extent allocation tree or chunk allocation\n"); + error( + "errors found in extent allocation tree or chunk allocation"); ret = repair_root_items(info); + err |= !!ret; if (ret < 0) goto close_out; if (repair) { @@ -11495,16 +12935,18 @@ int cmd_check(int argc, char **argv) fprintf(stderr, "Please run a filesystem check with the option --repair to fix them.\n"); ret = 1; + err |= !!ret; goto close_out; } if (!ctx.progress_enabled) { - if (btrfs_fs_compat_ro(info, BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE)) + if (btrfs_fs_compat_ro(info, FREE_SPACE_TREE)) fprintf(stderr, "checking free space tree\n"); else fprintf(stderr, "checking free space cache\n"); } ret = check_space_cache(root); + err |= !!ret; if (ret) goto out; @@ -11514,23 +12956,31 @@ int cmd_check(int argc, char **argv) * are no gaps in the file extents for inodes, otherwise we can just * ignore it when this happens. */ - no_holes = btrfs_fs_incompat(root->fs_info, - BTRFS_FEATURE_INCOMPAT_NO_HOLES); + no_holes = btrfs_fs_incompat(root->fs_info, NO_HOLES); if (!ctx.progress_enabled) fprintf(stderr, "checking fs roots\n"); - ret = check_fs_roots(root, &root_cache); + if (check_mode == CHECK_MODE_LOWMEM) + ret = check_fs_roots_v2(root->fs_info); + else + ret = check_fs_roots(root, &root_cache); + err |= !!ret; if (ret) goto out; fprintf(stderr, "checking csums\n"); ret = check_csums(root); + err |= !!ret; if (ret) goto out; fprintf(stderr, "checking root refs\n"); - ret = check_root_refs(root, &root_cache); - if (ret) - goto out; + /* For low memory mode, check_fs_roots_v2 handles root refs */ + if (check_mode != CHECK_MODE_LOWMEM) { + ret = check_root_refs(root, &root_cache); + err |= !!ret; + if (ret) + goto out; + } while (repair && !list_empty(&root->fs_info->recow_ebs)) { struct extent_buffer *eb; @@ -11539,6 +12989,7 @@ int cmd_check(int argc, char **argv) struct extent_buffer, recow); list_del_init(&eb->recow); ret = recow_extent_buffer(root, eb); + err |= !!ret; if (ret) break; } @@ -11548,32 +12999,33 @@ int cmd_check(int argc, char **argv) bad = list_first_entry(&delete_items, struct bad_item, list); list_del_init(&bad->list); - if (repair) + if (repair) { ret = delete_bad_item(root, bad); + err |= !!ret; + } free(bad); } if (info->quota_enabled) { - int err; fprintf(stderr, "checking quota groups\n"); - err = qgroup_verify_all(info); - if (err) + ret = qgroup_verify_all(info); + err |= !!ret; + if (ret) goto out; report_qgroups(0); - err = repair_qgroups(info, &qgroups_repaired); + ret = repair_qgroups(info, &qgroups_repaired); + err |= !!ret; if (err) goto out; + ret = 0; } if (!list_empty(&root->fs_info->recow_ebs)) { - fprintf(stderr, "Transid errors in file system\n"); + error("transid errors in file system"); ret = 1; + err |= !!ret; } out: - /* Don't override original ret */ - if (!ret && qgroups_repaired) - ret = qgroups_repaired; - if (found_old_backref) { /* * there was a disk format change when mixed * backref was in testing tree. The old format @@ -11583,7 +13035,7 @@ out: "The old format is not supported! *" "\n * Please mount the FS in readonly mode, " "backup data and re-format the FS. *\n\n"); - ret = 1; + err |= 1; } printf("found %llu bytes used err is %d\n", (unsigned long long)bytes_used, ret); @@ -11608,5 +13060,5 @@ err_out: if (ctx.progress_enabled) task_deinit(ctx.info); - return ret; + return err; } diff --git a/cmds-device.c b/cmds-device.c index a939c56f..de62cd42 100644 --- a/cmds-device.c +++ b/cmds-device.c @@ -283,7 +283,7 @@ static int cmd_device_scan(int argc, char **argv) if (all || argc - optind == 0) { printf("Scanning for Btrfs filesystems\n"); - ret = btrfs_scan_lblkid(); + ret = btrfs_scan_devices(); error_on(ret, "error %d while scanning", ret); ret = btrfs_register_all_devices(); error_on(ret, "there are %d errors while registering devices", ret); @@ -372,10 +372,13 @@ out: } static const char * const cmd_device_stats_usage[] = { - "btrfs device stats [-z] <path>|<device>", - "Show current device IO stats.", + "btrfs device stats [options] <path>|<device>", + "Show device IO error statistics", + "Show device IO error statistics for all devices of the given filesystem", + "identified by PATH or DEVICE. The filesystem must be mounted.", "", - "-z show current stats and reset values to zero", + "-c|--check return non-zero if any stat counter is not zero", + "-z|--reset show current stats and reset values to zero", NULL }; @@ -387,13 +390,26 @@ static int cmd_device_stats(int argc, char **argv) int ret; int fdmnt; int i; - int c; int err = 0; + int check = 0; __u64 flags = 0; DIR *dirstream = NULL; - while ((c = getopt(argc, argv, "z")) != -1) { + while (1) { + int c; + static const struct option long_options[] = { + {"reset", no_argument, NULL, 'z'}, + {NULL, 0, NULL, 0} + }; + + c = getopt_long(argc, argv, "cz", long_options, NULL); + if (c < 0) + break; + switch (c) { + case 'c': + check = 1; + break; case 'z': flags = BTRFS_DEV_STATS_RESET; break; @@ -414,7 +430,7 @@ static int cmd_device_stats(int argc, char **argv) ret = get_fs_info(dev_path, &fi_args, &di_args); if (ret) { - error("getting dev info for devstats failed: %s", + error("getting device info for %s failed: %s", dev_path, strerror(-ret)); err = 1; goto out; @@ -427,24 +443,37 @@ static int cmd_device_stats(int argc, char **argv) for (i = 0; i < fi_args.num_devices; i++) { struct btrfs_ioctl_get_dev_stats args = {0}; - __u8 path[BTRFS_DEVICE_PATH_NAME_MAX + 1]; + char path[BTRFS_DEVICE_PATH_NAME_MAX + 1]; - strncpy((char *)path, (char *)di_args[i].path, + strncpy(path, (char *)di_args[i].path, BTRFS_DEVICE_PATH_NAME_MAX); - path[BTRFS_DEVICE_PATH_NAME_MAX] = '\0'; + path[BTRFS_DEVICE_PATH_NAME_MAX] = 0; args.devid = di_args[i].devid; args.nr_items = BTRFS_DEV_STAT_VALUES_MAX; args.flags = flags; if (ioctl(fdmnt, BTRFS_IOC_GET_DEV_STATS, &args) < 0) { - error("DEV_STATS ioctl failed on %s: %s", + error("device stats ioctl failed on %s: %s", path, strerror(errno)); - err = 1; + err |= 1; } else { char *canonical_path; - - canonical_path = canonicalize_path((char *)path); + int j; + static const struct { + const char name[32]; + u64 num; + } dev_stats[] = { + { "write_io_errs", BTRFS_DEV_STAT_WRITE_ERRS }, + { "read_io_errs", BTRFS_DEV_STAT_READ_ERRS }, + { "flush_io_errs", BTRFS_DEV_STAT_FLUSH_ERRS }, + { "corruption_errs", + BTRFS_DEV_STAT_CORRUPTION_ERRS }, + { "generation_errs", + BTRFS_DEV_STAT_GENERATION_ERRS }, + }; + + canonical_path = canonicalize_path(path); /* No path when device is missing. */ if (!canonical_path) { @@ -457,31 +486,18 @@ static int cmd_device_stats(int argc, char **argv) "devid:%llu", args.devid); } - if (args.nr_items >= BTRFS_DEV_STAT_WRITE_ERRS + 1) - printf("[%s].write_io_errs %llu\n", - canonical_path, - (unsigned long long) args.values[ - BTRFS_DEV_STAT_WRITE_ERRS]); - if (args.nr_items >= BTRFS_DEV_STAT_READ_ERRS + 1) - printf("[%s].read_io_errs %llu\n", - canonical_path, - (unsigned long long) args.values[ - BTRFS_DEV_STAT_READ_ERRS]); - if (args.nr_items >= BTRFS_DEV_STAT_FLUSH_ERRS + 1) - printf("[%s].flush_io_errs %llu\n", - canonical_path, - (unsigned long long) args.values[ - BTRFS_DEV_STAT_FLUSH_ERRS]); - if (args.nr_items >= BTRFS_DEV_STAT_CORRUPTION_ERRS + 1) - printf("[%s].corruption_errs %llu\n", - canonical_path, - (unsigned long long) args.values[ - BTRFS_DEV_STAT_CORRUPTION_ERRS]); - if (args.nr_items >= BTRFS_DEV_STAT_GENERATION_ERRS + 1) - printf("[%s].generation_errs %llu\n", - canonical_path, - (unsigned long long) args.values[ - BTRFS_DEV_STAT_GENERATION_ERRS]); + for (j = 0; j < ARRAY_SIZE(dev_stats); j++) { + /* We got fewer items than we know */ + if (args.nr_items < dev_stats[j].num + 1) + continue; + printf("[%s].%-16s %llu\n", canonical_path, + dev_stats[j].name, + (unsigned long long) + args.values[dev_stats[j].num]); + if ((check == 1) + && (args.values[dev_stats[j].num] > 0)) + err |= 64; + } free(canonical_path); } diff --git a/cmds-fi-du.c b/cmds-fi-du.c index ec8e550f..2fc11945 100644 --- a/cmds-fi-du.c +++ b/cmds-fi-du.c @@ -27,8 +27,13 @@ #include <sys/ioctl.h> #include <linux/fs.h> +#include <linux/version.h> #include <linux/fiemap.h> +#if !defined(FIEMAP_EXTENT_SHARED) && (HAVE_OWN_FIEMAP_EXTENT_SHARED_DEFINE == 1) +#define FIEMAP_EXTENT_SHARED 0x00002000 +#endif + #include "utils.h" #include "commands.h" #include "kerncompat.h" @@ -79,7 +84,7 @@ static int add_shared_extent(u64 start, u64 len, struct rb_root *root) { struct shared_extent *sh; - BUG_ON(len == 0); + ASSERT(len != 0); sh = calloc(1, sizeof(*sh)); if (!sh) @@ -111,7 +116,7 @@ static void cleanup_shared_extents(struct rb_root *root) } } -#define dprintf(...) +#define dbgprintf(...) /* * Find all extents which overlap 'n', calculate the space @@ -123,7 +128,7 @@ static u64 count_unique_bytes(struct rb_root *root, struct shared_extent *n) u64 wstart = n->start; u64 wlast = n->last; - dprintf("Count overlaps:"); + dbgprintf("Count overlaps:"); do { /* @@ -136,7 +141,7 @@ static u64 count_unique_bytes(struct rb_root *root, struct shared_extent *n) if (wlast < n->last) wlast = n->last; - dprintf(" (%llu, %llu)", n->start, n->last); + dbgprintf(" (%llu, %llu)", n->start, n->last); tmp = n; n = extent_tree_iter_next(n, wstart, wlast); @@ -145,7 +150,7 @@ static u64 count_unique_bytes(struct rb_root *root, struct shared_extent *n) free(tmp); } while (n); - dprintf("; wstart: %llu wlast: %llu total: %llu\n", wstart, + dbgprintf("; wstart: %llu wlast: %llu total: %llu\n", wstart, wlast, wlast - wstart + 1); return wlast - wstart + 1; @@ -228,7 +233,7 @@ static int mark_inode_seen(u64 ino, u64 subvol) else if (cmp > 0) p = &(*p)->rb_right; else - BUG(); + return -EEXIST; } si = calloc(1, sizeof(*si)); @@ -326,6 +331,12 @@ static int du_calc_file_space(int fd, struct rb_root *shared_extents, if (flags & SKIP_FLAGS) continue; + if (ext_len == 0) { + warning("extent %llu has length 0, skipping", + (unsigned long long)fm_ext[i].fe_physical); + continue; + } + file_total += ext_len; if (flags & FIEMAP_EXTENT_SHARED) { file_shared += ext_len; @@ -448,7 +459,7 @@ static int du_add_file(const char *filename, int dirfd, goto out; } - ret = lookup_ino_rootid(fd, &subvol); + ret = lookup_path_rootid(fd, &subvol); if (ret) goto out_close; @@ -540,6 +551,7 @@ int cmd_filesystem_du(int argc, char **argv) { int ret = 0, err = 0; int i; + u32 kernel_version; unit_mode = get_unit_mode_from_arg(&argc, argv, 1); @@ -564,6 +576,14 @@ int cmd_filesystem_du(int argc, char **argv) if (check_argc_min(argc - optind, 1)) usage(cmd_filesystem_du_usage); + kernel_version = get_running_kernel_version(); + + if (kernel_version < KERNEL_VERSION(2,6,33)) { + warning( +"old kernel version detected, shared space will be reported as exclusive\n" +"due to missing support for FIEMAP_EXTENT_SHARED flag"); + } + printf("%10s %10s %10s %s\n", "Total", "Exclusive", "Set shared", "Filename"); diff --git a/cmds-fi-usage.c b/cmds-fi-usage.c index 04d68b18..88e346ad 100644 --- a/cmds-fi-usage.c +++ b/cmds-fi-usage.c @@ -471,7 +471,7 @@ static int print_filesystem_usage_overall(int fd, struct chunk_info *chunkinfo, printf(" Device allocated:\t\t%*s\n", width, pretty_size_mode(r_total_chunks, unit_mode)); printf(" Device unallocated:\t\t%*s\n", width, - pretty_size_mode(r_total_unused, unit_mode)); + pretty_size_mode(r_total_unused, unit_mode | UNITS_NEGATIVE)); printf(" Device missing:\t\t%*s\n", width, pretty_size_mode(r_total_missing, unit_mode)); printf(" Used:\t\t\t%*s\n", width, @@ -535,7 +535,11 @@ static int load_device_info(int fd, struct device_info **device_info_ptr, } for (i = 0, ndevs = 0 ; i <= fi_args.max_id ; i++) { - BUG_ON(ndevs >= fi_args.num_devices); + if (ndevs >= fi_args.num_devices) { + error("unexpected number of devices: %d >= %llu", ndevs, + (unsigned long long)fi_args.num_devices); + goto out; + } memset(&dev_info, 0, sizeof(dev_info)); ret = get_device_info(fd, i, &dev_info); @@ -543,8 +547,7 @@ static int load_device_info(int fd, struct device_info **device_info_ptr, continue; if (ret) { error("cannot get info about device devid=%d", i); - free(info); - return ret; + goto out; } info[ndevs].devid = dev_info.devid; @@ -559,7 +562,12 @@ static int load_device_info(int fd, struct device_info **device_info_ptr, ++ndevs; } - BUG_ON(ndevs != fi_args.num_devices); + if (ndevs != fi_args.num_devices) { + error("unexpected number of devices: %d != %llu", ndevs, + (unsigned long long)fi_args.num_devices); + goto out; + } + qsort(info, fi_args.num_devices, sizeof(struct device_info), cmp_device_info); @@ -567,6 +575,10 @@ static int load_device_info(int fd, struct device_info **device_info_ptr, *device_info_ptr = info; return 0; + +out: + free(info); + return ret; } int load_chunk_and_device_info(int fd, struct chunk_info **chunkinfo, @@ -724,8 +736,8 @@ static void _cmd_filesystem_usage_tabular(unsigned unit_mode, unused = get_partition_size(device_info_ptr[i].path) - total_allocated; - table_printf(matrix, unallocated_col, vhdr_skip + i, - ">%s", pretty_size_mode(unused, unit_mode)); + table_printf(matrix, unallocated_col, vhdr_skip + i, ">%s", + pretty_size_mode(unused, unit_mode | UNITS_NEGATIVE)); total_unused += unused; } @@ -759,7 +771,8 @@ static void _cmd_filesystem_usage_tabular(unsigned unit_mode, } table_printf(matrix, unallocated_col, vhdr_skip + device_info_count + 1, - ">%s", pretty_size_mode(total_unused, unit_mode)); + ">%s", + pretty_size_mode(total_unused, unit_mode | UNITS_NEGATIVE)); table_printf(matrix, 1, vhdr_skip + device_info_count + 2, "<Used"); for (i = 0, col = spaceinfos_col; i < sargs->total_spaces; i++) { @@ -872,7 +885,7 @@ static void _cmd_filesystem_usage_linear(unsigned unit_mode, printf("Unallocated:\n"); print_unused(info_ptr, info_count, device_info_ptr, device_info_count, - unit_mode); + unit_mode | UNITS_NEGATIVE); } static int print_filesystem_usage_by_chunk(int fd, @@ -1015,7 +1028,8 @@ void print_device_chunks(int fd, struct device_info *devinfo, } printf(" Unallocated: %*s%10s\n", (int)(20 - strlen("Unallocated")), "", - pretty_size_mode(devinfo->size - allocated, unit_mode)); + pretty_size_mode(devinfo->size - allocated, + unit_mode | UNITS_NEGATIVE)); } void print_device_sizes(int fd, struct device_info *devinfo, unsigned unit_mode) diff --git a/cmds-filesystem.c b/cmds-filesystem.c index 90eccf70..e7d31364 100644 --- a/cmds-filesystem.c +++ b/cmds-filesystem.c @@ -149,7 +149,7 @@ static int get_df(int fd, struct btrfs_ioctl_space_args **sargs_ret) ret = ioctl(fd, BTRFS_IOC_SPACE_INFO, sargs); if (ret < 0) { - error("cannot get space info: %s\n", strerror(errno)); + error("cannot get space info: %s", strerror(errno)); free(sargs); return -errno; } @@ -248,7 +248,7 @@ static int match_search_item_kernel(__u8 *fsid, char *mnt, char *label, return 0; } -static int uuid_search(struct btrfs_fs_devices *fs_devices, char *search) +static int uuid_search(struct btrfs_fs_devices *fs_devices, const char *search) { char uuidbuf[BTRFS_UUID_UNPARSED_SIZE]; struct list_head *cur; @@ -495,7 +495,7 @@ static int btrfs_scan_kernel(void *search, unsigned unit_mode) if ((fd != -1) && !get_df(fd, &space_info_arg)) { print_one_fs(&fs_info_arg, dev_info_arg, space_info_arg, label, unit_mode); - kfree(space_info_arg); + free(space_info_arg); memset(label, 0, sizeof(label)); found = 1; } @@ -509,7 +509,7 @@ out: return !found; } -static int dev_to_fsid(char *dev, __u8 *fsid) +static int dev_to_fsid(const char *dev, __u8 *fsid) { struct btrfs_super_block *disk_super; char buf[BTRFS_SUPER_INFO_SIZE]; @@ -872,10 +872,10 @@ static int cmd_filesystem_show(int argc, char **argv) goto out; devs_only: - ret = btrfs_scan_lblkid(); + ret = btrfs_scan_devices(); if (ret) { - error("blkid device scan returned %d\n", ret); + error("blkid device scan returned %d", ret); return 1; } @@ -972,20 +972,6 @@ static const char * const cmd_filesystem_defrag_usage[] = { NULL }; -static int do_defrag(int fd, int fancy_ioctl, - struct btrfs_ioctl_defrag_range_args *range) -{ - int ret; - - if (!fancy_ioctl) - ret = ioctl(fd, BTRFS_IOC_DEFRAG, NULL); - else - ret = ioctl(fd, BTRFS_IOC_DEFRAG_RANGE, range); - - return ret; -} - -static int defrag_global_fancy_ioctl; static struct btrfs_ioctl_defrag_range_args defrag_global_range; static int defrag_global_verbose; static int defrag_global_errors; @@ -1004,12 +990,11 @@ static int defrag_callback(const char *fpath, const struct stat *sb, err = errno; goto error; } - ret = do_defrag(fd, defrag_global_fancy_ioctl, &defrag_global_range); + ret = ioctl(fd, BTRFS_IOC_DEFRAG_RANGE, &defrag_global_range); close(fd); - if (ret && errno == ENOTTY && defrag_global_fancy_ioctl) { - error("defrag range ioctl not " - "supported in this kernel, please try " - "without any options."); + if (ret && errno == ENOTTY) { + error( +"defrag range ioctl not supported in this kernel version, 2.6.33 and newer is required"); defrag_global_errors++; return ENOTTY; } @@ -1036,21 +1021,20 @@ static int cmd_filesystem_defrag(int argc, char **argv) int i; int recursive = 0; int ret = 0; - int e = 0; int compress_type = BTRFS_COMPRESS_NONE; DIR *dirstream; /* * Kernel has a different default (256K) that is supposed to be safe, * but it does not defragment very well. The 32M will likely lead to - * better results and is independent of the kernel default. + * better results and is independent of the kernel default. We have to + * use the v2 defrag ioctl. */ thresh = 32 * 1024 * 1024; defrag_global_errors = 0; defrag_global_verbose = 0; defrag_global_errors = 0; - defrag_global_fancy_ioctl = 0; while(1) { int c = getopt(argc, argv, "vrc::fs:l:t:"); if (c < 0) @@ -1061,22 +1045,18 @@ static int cmd_filesystem_defrag(int argc, char **argv) compress_type = BTRFS_COMPRESS_ZLIB; if (optarg) compress_type = parse_compress_type(optarg); - defrag_global_fancy_ioctl = 1; break; case 'f': flush = 1; - defrag_global_fancy_ioctl = 1; break; case 'v': defrag_global_verbose = 1; break; case 's': start = parse_size(optarg); - defrag_global_fancy_ioctl = 1; break; case 'l': len = parse_size(optarg); - defrag_global_fancy_ioctl = 1; break; case 't': thresh = parse_size(optarg); @@ -1086,7 +1066,6 @@ static int cmd_filesystem_defrag(int argc, char **argv) thresh, (u32)-1); thresh = (u32)-1; } - defrag_global_fancy_ioctl = 1; break; case 'r': recursive = 1; @@ -1110,13 +1089,43 @@ static int cmd_filesystem_defrag(int argc, char **argv) if (flush) defrag_global_range.flags |= BTRFS_DEFRAG_RANGE_START_IO; + /* + * Look for directory arguments and warn if the recursive mode is not + * requested, as this is not implemented as recursive defragmentation + * in kernel. The stat errors are silent here as we check them below. + */ + if (!recursive) { + int found = 0; + + for (i = optind; i < argc; i++) { + struct stat st; + + if (stat(argv[i], &st)) + continue; + + if (S_ISDIR(st.st_mode)) { + warning( + "directory specified but recursive mode not requested: %s", + argv[i]); + found = 1; + } + } + if (found) { + warning( +"a directory passed to the defrag ioctl will not process the files\n" +"recursively but will defragment the subvolume tree and the extent tree.\n" +"If this is not intended, please use option -r ."); + } + } + for (i = optind; i < argc; i++) { struct stat st; + int defrag_err = 0; dirstream = NULL; fd = open_file_or_dir(argv[i], &dirstream); if (fd < 0) { - error("cannot open %s: %s\n", argv[i], + error("cannot open %s: %s", argv[i], strerror(errno)); defrag_global_errors++; close_file_or_dir(fd, dirstream); @@ -1130,44 +1139,36 @@ static int cmd_filesystem_defrag(int argc, char **argv) continue; } if (!(S_ISDIR(st.st_mode) || S_ISREG(st.st_mode))) { - error("%s is not a directory or a regular file\n", + error("%s is not a directory or a regular file", argv[i]); defrag_global_errors++; close_file_or_dir(fd, dirstream); continue; } - if (recursive) { - if (S_ISDIR(st.st_mode)) { - ret = nftw(argv[i], defrag_callback, 10, + if (recursive && S_ISDIR(st.st_mode)) { + ret = nftw(argv[i], defrag_callback, 10, FTW_MOUNT | FTW_PHYS); - if (ret == ENOTTY) - exit(1); - /* errors are handled in the callback */ - ret = 0; - } else { - if (defrag_global_verbose) - printf("%s\n", argv[i]); - ret = do_defrag(fd, defrag_global_fancy_ioctl, - &defrag_global_range); - e = errno; - } + if (ret == ENOTTY) + exit(1); + /* errors are handled in the callback */ + ret = 0; } else { if (defrag_global_verbose) printf("%s\n", argv[i]); - ret = do_defrag(fd, defrag_global_fancy_ioctl, + ret = ioctl(fd, BTRFS_IOC_DEFRAG_RANGE, &defrag_global_range); - e = errno; + defrag_err = errno; } close_file_or_dir(fd, dirstream); - if (ret && e == ENOTTY && defrag_global_fancy_ioctl) { - error("defrag range ioctl not " - "supported in this kernel, please try " - "without any options."); + if (ret && defrag_err == ENOTTY) { + error( +"defrag range ioctl not supported in this kernel version, 2.6.33 and newer is required"); defrag_global_errors++; break; } if (ret) { - error("defrag failed on %s: %s", argv[i], strerror(e)); + error("defrag failed on %s: %s", argv[i], + strerror(defrag_err)); defrag_global_errors++; } } diff --git a/cmds-inspect-dump-super.c b/cmds-inspect-dump-super.c index 0ae740a2..ba0d708e 100644 --- a/cmds-inspect-dump-super.c +++ b/cmds-inspect-dump-super.c @@ -37,7 +37,7 @@ static int check_csum_sblock(void *sb, int csum_size) { - char result[BTRFS_CSUM_SIZE]; + u8 result[BTRFS_CSUM_SIZE]; u32 crc = ~(u32)0; crc = btrfs_csum_data(NULL, (char *)sb + BTRFS_CSUM_SIZE, @@ -100,20 +100,19 @@ static void print_sys_chunk_array(struct btrfs_super_block *sb) if (cur_offset + len > array_size) goto out_short_read; - print_chunk(buf, chunk); num_stripes = btrfs_chunk_num_stripes(buf, chunk); if (!num_stripes) { - printk( - "ERROR: invalid number of stripes %u in sys_array at offset %u\n", + error( + "invalid number of stripes %u in sys_array at offset %u", num_stripes, cur_offset); break; } len = btrfs_chunk_item_size(num_stripes); if (cur_offset + len > array_size) goto out_short_read; + print_chunk(buf, chunk); } else { - printk( - "ERROR: unexpected item type %u in sys_array at offset %u\n", + error("unexpected item type %u in sys_array at offset %u", (u32)key.type, cur_offset); break; } @@ -129,7 +128,7 @@ out: return; out_short_read: - printk("ERROR: sys_array too short to read %u bytes at offset %u\n", + error("sys_array too short to read %u bytes at offset %u", len, cur_offset); free(buf); } @@ -198,6 +197,16 @@ struct readable_flag_entry { char *output; }; +#define DEF_COMPAT_RO_FLAG_ENTRY(bit_name) \ + {BTRFS_FEATURE_COMPAT_RO_##bit_name, #bit_name} + +static struct readable_flag_entry compat_ro_flags_array[] = { + DEF_COMPAT_RO_FLAG_ENTRY(FREE_SPACE_TREE), + DEF_COMPAT_RO_FLAG_ENTRY(FREE_SPACE_TREE_VALID), +}; +static const int compat_ro_flags_num = sizeof(compat_ro_flags_array) / + sizeof(struct readable_flag_entry); + #define DEF_INCOMPAT_FLAG_ENTRY(bit_name) \ {BTRFS_FEATURE_INCOMPAT_##bit_name, #bit_name} @@ -269,6 +278,19 @@ static void __print_readable_flag(u64 flag, struct readable_flag_entry *array, printf(")\n"); } +static void print_readable_compat_ro_flag(u64 flag) +{ + /* + * We know about the FREE_SPACE_TREE{,_VALID} bits, but we don't + * actually support them yet. + */ + return __print_readable_flag(flag, compat_ro_flags_array, + compat_ro_flags_num, + BTRFS_FEATURE_COMPAT_RO_SUPP | + BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE | + BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE_VALID); +} + static void print_readable_incompat_flag(u64 flag) { return __print_readable_flag(flag, incompat_flags_array, @@ -378,6 +400,7 @@ static void dump_superblock(struct btrfs_super_block *sb, int full) (unsigned long long)btrfs_super_compat_flags(sb)); printf("compat_ro_flags\t\t0x%llx\n", (unsigned long long)btrfs_super_compat_ro_flags(sb)); + print_readable_compat_ro_flag(btrfs_super_compat_ro_flags(sb)); printf("incompat_flags\t\t0x%llx\n", (unsigned long long)btrfs_super_incompat_flags(sb)); print_readable_incompat_flag(btrfs_super_incompat_flags(sb)); diff --git a/cmds-inspect-dump-tree.c b/cmds-inspect-dump-tree.c index 5e206345..df7be617 100644 --- a/cmds-inspect-dump-tree.c +++ b/cmds-inspect-dump-tree.c @@ -35,6 +35,7 @@ static void print_extents(struct btrfs_root *root, struct extent_buffer *eb) { + struct extent_buffer *next; int i; u32 nr; u32 size; @@ -50,21 +51,32 @@ static void print_extents(struct btrfs_root *root, struct extent_buffer *eb) size = root->nodesize; nr = btrfs_header_nritems(eb); for (i = 0; i < nr; i++) { - struct extent_buffer *next = read_tree_block(root, - btrfs_node_blockptr(eb, i), - size, - btrfs_node_ptr_generation(eb, i)); + next = read_tree_block(root, btrfs_node_blockptr(eb, i), + size, btrfs_node_ptr_generation(eb, i)); if (!extent_buffer_uptodate(next)) continue; - if (btrfs_is_leaf(next) && - btrfs_header_level(eb) != 1) - BUG(); - if (btrfs_header_level(next) != - btrfs_header_level(eb) - 1) - BUG(); + 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; + } print_extents(root, next); free_extent_buffer(next); } + + return; + +out: + free_extent_buffer(next); } static void print_old_roots(struct btrfs_super_block *super) @@ -248,26 +260,27 @@ int cmd_inspect_dump_tree(int argc, char **argv) case 'b': block_only = arg_strtou64(optarg); break; - case 't': - if (string_is_numerical(optarg)) { - tree_id = arg_strtou64(optarg); - } else { - const char *end = NULL; + case 't': { + const char *end = NULL; + if (string_is_numerical(optarg)) + tree_id = arg_strtou64(optarg); + else tree_id = treeid_from_string(optarg, &end); - if (*end) { - error("unexpected tree id suffix of '%s': %s\n", - optarg, end); - exit(1); - } - } if (!tree_id) { - error("unrecognized tree id: %s\n", + error("unrecognized tree id: %s", optarg); exit(1); } + + if (end && *end) { + error("unexpected tree id suffix of '%s': %s", + optarg, end); + exit(1); + } break; + } default: usage(cmd_inspect_dump_tree_usage); } @@ -376,9 +389,14 @@ again: key.offset = 0; key.objectid = 0; - btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY); + key.type = BTRFS_ROOT_ITEM_KEY; ret = btrfs_search_slot(NULL, tree_root_scan, &key, &path, 0, 0); - BUG_ON(ret < 0); + if (ret < 0) { + error("cannot read ROOT_ITEM from tree %llu: %s", + (unsigned long long)tree_root_scan->root_key.objectid, + strerror(-ret)); + goto close_root; + } while (1) { leaf = path.nodes[0]; slot = path.slots[0]; @@ -391,7 +409,7 @@ again: } btrfs_item_key(leaf, &disk_key, path.slots[0]); btrfs_disk_key_to_cpu(&found_key, &disk_key); - if (btrfs_key_type(&found_key) == BTRFS_ROOT_ITEM_KEY) { + if (found_key.type == BTRFS_ROOT_ITEM_KEY) { unsigned long offset; struct extent_buffer *buf; int skip = extent_only | device_only | uuid_tree_only; @@ -525,8 +543,7 @@ next: no_node: btrfs_release_path(&path); - if (tree_root_scan == info->tree_root && - info->log_root_tree) { + if (tree_root_scan == info->tree_root && info->log_root_tree) { tree_root_scan = info->log_root_tree; goto again; } diff --git a/cmds-inspect-tree-stats.c b/cmds-inspect-tree-stats.c index cd7ef3ba..0e2786c9 100644 --- a/cmds-inspect-tree-stats.c +++ b/cmds-inspect-tree-stats.c @@ -155,7 +155,7 @@ static int walk_nodes(struct btrfs_root *root, struct btrfs_path *path, root->nodesize, btrfs_node_ptr_generation(b, i)); if (!extent_buffer_uptodate(tmp)) { - fprintf(stderr, "Failed to read blocknr %llu\n", + error("failed to read blocknr %llu", btrfs_node_blockptr(b, i)); continue; } @@ -175,7 +175,8 @@ static int walk_nodes(struct btrfs_root *root, struct btrfs_path *path, if (stat->max_seek_len < distance) stat->max_seek_len = distance; if (add_seek(&stat->seek_root, distance)) { - fprintf(stderr, "Error adding new seek\n"); + error("cannot add new seek at distance %llu", + (unsigned long long)distance); ret = -ENOMEM; break; } @@ -203,7 +204,7 @@ static int walk_nodes(struct btrfs_root *root, struct btrfs_path *path, stat->highest_bytenr = cur_blocknr; free_extent_buffer(tmp); if (ret) { - fprintf(stderr, "Error walking down path\n"); + error("walking down path failed: %d", ret); break; } } @@ -312,7 +313,7 @@ static int calc_root_size(struct btrfs_root *tree_root, struct btrfs_key *key, int find_inline) { struct btrfs_root *root; - struct btrfs_path *path; + struct btrfs_path path; struct rb_node *n; struct timeval start, end, diff = {0}; struct root_stats stat; @@ -322,39 +323,34 @@ static int calc_root_size(struct btrfs_root *tree_root, struct btrfs_key *key, root = btrfs_read_fs_root(tree_root->fs_info, key); if (IS_ERR(root)) { - fprintf(stderr, "Failed to read root %llu\n", key->objectid); - return 1; - } - - path = btrfs_alloc_path(); - if (!path) { - fprintf(stderr, "Could not allocate path\n"); + error("failed to read root %llu", key->objectid); return 1; } + btrfs_init_path(&path); memset(&stat, 0, sizeof(stat)); level = btrfs_header_level(root->node); stat.lowest_bytenr = btrfs_header_bytenr(root->node); stat.highest_bytenr = stat.lowest_bytenr; stat.min_cluster_size = (u64)-1; stat.max_cluster_size = root->nodesize; - path->nodes[level] = root->node; + path.nodes[level] = root->node; if (gettimeofday(&start, NULL)) { - fprintf(stderr, "Error getting time: %d\n", errno); + error("cannot get time: %s", strerror(errno)); goto out; } if (!level) { - ret = walk_leaf(root, path, &stat, find_inline); + ret = walk_leaf(root, &path, &stat, find_inline); if (ret) goto out; goto out_print; } - ret = walk_nodes(root, path, &stat, level, find_inline); + ret = walk_nodes(root, &path, &stat, level, find_inline); if (ret) goto out; if (gettimeofday(&end, NULL)) { - fprintf(stderr, "Error getting time: %d\n", errno); + error("cannot get time: %s", strerror(errno)); goto out; } timeval_subtract(&diff, &end, &start); @@ -416,13 +412,11 @@ out: } /* - * We only use path to save node data in iterating, - * without holding eb's ref_cnt in path. - * Don't use btrfs_free_path() here, it will free these - * eb again, and cause many problems, as negative ref_cnt - * or invalid memory access. + * We only use path to save node data in iterating, without holding + * eb's ref_cnt in path. Don't use btrfs_release_path() here, it will + * free these eb again, and cause many problems, as negative ref_cnt or + * invalid memory access. */ - free(path); return ret; } @@ -468,7 +462,7 @@ int cmd_inspect_tree_stats(int argc, char **argv) root = open_ctree(argv[optind], 0, 0); if (!root) { - fprintf(stderr, "Couldn't open ctree\n"); + error("cannot open ctree"); exit(1); } diff --git a/cmds-inspect.c b/cmds-inspect.c index 4b7cea07..5e58a284 100644 --- a/cmds-inspect.c +++ b/cmds-inspect.c @@ -146,7 +146,7 @@ static int cmd_inspect_logical_resolve(int argc, char **argv) struct btrfs_ioctl_logical_ino_args loi; struct btrfs_data_container *inodes; u64 size = 4096; - char full_path[4096]; + char full_path[PATH_MAX]; char *path_ptr; DIR *dirstream = NULL; @@ -207,7 +207,10 @@ static int cmd_inspect_logical_resolve(int argc, char **argv) ret = snprintf(full_path, bytes_left, "%s/", argv[optind+1]); path_ptr = full_path + ret; bytes_left -= ret + 1; - BUG_ON(bytes_left < 0); + if (bytes_left < 0) { + error("path buffer too small: %d bytes", bytes_left); + goto out; + } for (i = 0; i < inodes->elem_cnt; i += 3) { u64 inum = inodes->val[i]; @@ -230,8 +233,12 @@ static int cmd_inspect_logical_resolve(int argc, char **argv) path_ptr[-1] = '/'; ret = snprintf(path_ptr, bytes_left, "%s", name); - BUG_ON(ret >= bytes_left); free(name); + if (ret >= bytes_left) { + error("path buffer too small: %d bytes", + bytes_left - ret); + goto out; + } path_fd = btrfs_open_dir(full_path, &dirs, 1); if (path_fd < 0) { ret = -ENOENT; @@ -319,7 +326,7 @@ static int cmd_inspect_rootid(int argc, char **argv) goto out; } - ret = lookup_ino_rootid(fd, &rootid); + ret = lookup_path_rootid(fd, &rootid); if (ret) { error("failed to lookup root id: %s", strerror(-ret)); goto out; diff --git a/cmds-property.c b/cmds-property.c index e59882b4..854bff56 100644 --- a/cmds-property.c +++ b/cmds-property.c @@ -199,12 +199,6 @@ out: return ret; } -static int print_prop_help(const struct prop_handler *prop) -{ - fprintf(stdout, "%-20s%s\n", prop->name, prop->desc); - return 0; -} - static int dump_prop(const struct prop_handler *prop, const char *object, int types, @@ -217,7 +211,7 @@ static int dump_prop(const struct prop_handler *prop, if (!name_and_help) ret = prop->handler(type, object, prop->name, NULL); else - ret = print_prop_help(prop); + printf("%-20s%s\n", prop->name, prop->desc); } return ret; } diff --git a/cmds-qgroup.c b/cmds-qgroup.c index a3bc939a..f4503fd9 100644 --- a/cmds-qgroup.c +++ b/cmds-qgroup.c @@ -272,8 +272,7 @@ static int cmd_qgroup_destroy(int argc, char **argv) } static const char * const cmd_qgroup_show_usage[] = { - "btrfs qgroup show -pcreFf " - "[--sort=qgroupid,rfer,excl,max_rfer,max_excl] <path>", + "btrfs qgroup show [options] <path>", "Show subvolume quota groups.", "-p print parent qgroup id", "-c print child qgroup id", @@ -288,6 +287,7 @@ static const char * const cmd_qgroup_show_usage[] = { " list qgroups sorted by specified items", " you can use '+' or '-' in front of each item.", " (+:ascending, -:descending, ascending default)", + "--sync force sync of the filesystem before getting info", NULL }; @@ -296,11 +296,11 @@ static int cmd_qgroup_show(int argc, char **argv) char *path; int ret = 0; int fd; - int e; DIR *dirstream = NULL; u64 qgroupid; int filter_flag = 0; unsigned unit_mode; + int sync = 0; struct btrfs_qgroup_comparer_set *comparer_set; struct btrfs_qgroup_filter_set *filter_set; @@ -311,8 +311,13 @@ static int cmd_qgroup_show(int argc, char **argv) while (1) { int c; + enum { + GETOPT_VAL_SORT = 256, + GETOPT_VAL_SYNC + }; static const struct option long_options[] = { - {"sort", required_argument, NULL, 'S'}, + {"sort", required_argument, NULL, GETOPT_VAL_SORT}, + {"sync", no_argument, NULL, GETOPT_VAL_SYNC}, { NULL, 0, NULL, 0 } }; @@ -342,12 +347,15 @@ static int cmd_qgroup_show(int argc, char **argv) case 'f': filter_flag |= 0x2; break; - case 'S': + case GETOPT_VAL_SORT: ret = btrfs_qgroup_parse_sort_string(optarg, &comparer_set); if (ret) usage(cmd_qgroup_show_usage); break; + case GETOPT_VAL_SYNC: + sync = 1; + break; default: usage(cmd_qgroup_show_usage); } @@ -360,13 +368,26 @@ static int cmd_qgroup_show(int argc, char **argv) path = argv[optind]; fd = btrfs_open_dir(path, &dirstream, 1); if (fd < 0) { - btrfs_qgroup_free_filter_set(filter_set); - btrfs_qgroup_free_comparer_set(comparer_set); + free(filter_set); + free(comparer_set); return 1; } + if (sync) { + ret = ioctl(fd, BTRFS_IOC_SYNC); + if (ret < 0) + warning("sync ioctl failed on '%s': %s", path, + strerror(errno)); + } + if (filter_flag) { - qgroupid = btrfs_get_path_rootid(fd); + ret = lookup_path_rootid(fd, &qgroupid); + if (ret < 0) { + error("cannot resolve rootid for %s: %s", + path, strerror(-ret)); + close_file_or_dir(fd, dirstream); + goto out; + } if (filter_flag & 0x1) btrfs_qgroup_setup_filter(&filter_set, BTRFS_QGROUP_FILTER_ALL_PARENT, @@ -377,11 +398,13 @@ static int cmd_qgroup_show(int argc, char **argv) qgroupid); } ret = btrfs_show_qgroups(fd, filter_set, comparer_set); - e = errno; + if (ret == -ENOENT) + error("can't list qgroups: quotas not enabled"); + else if (ret < 0) + error("can't list qgroups: %s", strerror(-ret)); close_file_or_dir(fd, dirstream); - if (ret < 0) - error("can't list qgroups: %s", strerror(e)); +out: return !!ret; } diff --git a/cmds-quota.c b/cmds-quota.c index 75c032b1..f9b422dc 100644 --- a/cmds-quota.c +++ b/cmds-quota.c @@ -154,28 +154,42 @@ static int cmd_quota_rescan(int argc, char **argv) ret = ioctl(fd, ioctlnum, &args); e = errno; - if (wait_for_completion && (ret == 0 || e == EINPROGRESS)) { - ret = ioctl(fd, BTRFS_IOC_QUOTA_RESCAN_WAIT, &args); - e = errno; - } - close_file_or_dir(fd, dirstream); - - if (ioctlnum == BTRFS_IOC_QUOTA_RESCAN) { + if (ioctlnum == BTRFS_IOC_QUOTA_RESCAN_STATUS) { + close_file_or_dir(fd, dirstream); if (ret < 0) { - error("quota rescan failed: %s", strerror(e)); + error("could not obtain quota rescan status: %s", + strerror(e)); return 1; - } else { - printf("quota rescan started\n"); } - } else { - if (!args.flags) { + if (!args.flags) printf("no rescan operation in progress\n"); - } else { + else printf("rescan operation running (current key %lld)\n", args.progress); + return 0; + } + + if (ret == 0) { + printf("quota rescan started\n"); + fflush(stdout); + } else if (ret < 0 && (!wait_for_completion || e != EINPROGRESS)) { + error("quota rescan failed: %s", strerror(e)); + close_file_or_dir(fd, dirstream); + return 1; + } + + if (wait_for_completion) { + ret = ioctl(fd, BTRFS_IOC_QUOTA_RESCAN_WAIT, &args); + e = errno; + if (ret < 0) { + error("quota rescan wait failed: %s", + strerror(e)); + close_file_or_dir(fd, dirstream); + return 1; } } + close_file_or_dir(fd, dirstream); return 0; } diff --git a/cmds-receive.c b/cmds-receive.c index f4a3a4f1..166d37dc 100644 --- a/cmds-receive.c +++ b/cmds-receive.c @@ -49,6 +49,7 @@ #include "send.h" #include "send-stream.h" #include "send-utils.h" +#include "send-dump.h" static int g_verbose = 0; @@ -86,7 +87,7 @@ struct btrfs_receive int cached_capabilities_len; }; -static int finish_subvol(struct btrfs_receive *r) +static int finish_subvol(struct btrfs_receive *rctx) { int ret; int subvol_fd = -1; @@ -94,21 +95,21 @@ static int finish_subvol(struct btrfs_receive *r) char uuid_str[BTRFS_UUID_UNPARSED_SIZE]; u64 flags; - if (r->cur_subvol_path[0] == 0) + if (rctx->cur_subvol_path[0] == 0) return 0; - subvol_fd = openat(r->mnt_fd, r->cur_subvol_path, - O_RDONLY | O_NOATIME); + subvol_fd = openat(rctx->mnt_fd, rctx->cur_subvol_path, + O_RDONLY | O_NOATIME); if (subvol_fd < 0) { ret = -errno; - error("cannot open %s: %s\n", - r->cur_subvol_path, strerror(-ret)); + error("cannot open %s: %s", + rctx->cur_subvol_path, strerror(-ret)); goto out; } memset(&rs_args, 0, sizeof(rs_args)); - memcpy(rs_args.uuid, r->cur_subvol.received_uuid, BTRFS_UUID_SIZE); - rs_args.stransid = r->cur_subvol.stransid; + memcpy(rs_args.uuid, rctx->cur_subvol.received_uuid, BTRFS_UUID_SIZE); + rs_args.stransid = rctx->cur_subvol.stransid; if (g_verbose >= 1) { uuid_unparse((u8*)rs_args.uuid, uuid_str); @@ -123,7 +124,7 @@ static int finish_subvol(struct btrfs_receive *r) strerror(-ret)); goto out; } - r->cur_subvol.rtransid = rs_args.rtransid; + rctx->cur_subvol.rtransid = rs_args.rtransid; ret = ioctl(subvol_fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags); if (ret < 0) { @@ -146,8 +147,8 @@ static int finish_subvol(struct btrfs_receive *r) ret = 0; out: - if (r->cur_subvol_path[0]) { - r->cur_subvol_path[0] = 0; + if (rctx->cur_subvol_path[0]) { + rctx->cur_subvol_path[0] = 0; } if (subvol_fd != -1) close(subvol_fd); @@ -158,28 +159,39 @@ static int process_subvol(const char *path, const u8 *uuid, u64 ctransid, void *user) { int ret; - struct btrfs_receive *r = user; + struct btrfs_receive *rctx = user; struct btrfs_ioctl_vol_args args_v1; char uuid_str[BTRFS_UUID_UNPARSED_SIZE]; - ret = finish_subvol(r); + ret = finish_subvol(rctx); if (ret < 0) goto out; - BUG_ON(r->cur_subvol.path); - BUG_ON(r->cur_subvol_path[0]); + if (rctx->cur_subvol.path) { + error("subvol: another one already started, path ptr: %s", + rctx->cur_subvol.path); + ret = -EINVAL; + goto out; + } + if (rctx->cur_subvol_path[0]) { + error("subvol: another one already started, path buf: %s", + rctx->cur_subvol.path); + ret = -EINVAL; + goto out; + } - if (*r->dest_dir_path == 0) { - strncpy_null(r->cur_subvol_path, path); + if (*rctx->dest_dir_path == 0) { + strncpy_null(rctx->cur_subvol_path, path); } else { - ret = path_cat_out(r->cur_subvol_path, r->dest_dir_path, path); + ret = path_cat_out(rctx->cur_subvol_path, rctx->dest_dir_path, + path); if (ret < 0) { - error("subvol: path invalid: %s\n", path); + error("subvol: path invalid: %s", path); goto out; } } - ret = path_cat3_out(r->full_subvol_path, r->root_path, - r->dest_dir_path, path); + ret = path_cat3_out(rctx->full_subvol_path, rctx->root_path, + rctx->dest_dir_path, path); if (ret < 0) { error("subvol: path invalid: %s", path); goto out; @@ -187,19 +199,19 @@ static int process_subvol(const char *path, const u8 *uuid, u64 ctransid, fprintf(stderr, "At subvol %s\n", path); - memcpy(r->cur_subvol.received_uuid, uuid, BTRFS_UUID_SIZE); - r->cur_subvol.stransid = ctransid; + memcpy(rctx->cur_subvol.received_uuid, uuid, BTRFS_UUID_SIZE); + rctx->cur_subvol.stransid = ctransid; if (g_verbose) { - uuid_unparse((u8*)r->cur_subvol.received_uuid, uuid_str); + uuid_unparse((u8*)rctx->cur_subvol.received_uuid, uuid_str); fprintf(stderr, "receiving subvol %s uuid=%s, stransid=%llu\n", path, uuid_str, - r->cur_subvol.stransid); + rctx->cur_subvol.stransid); } memset(&args_v1, 0, sizeof(args_v1)); strncpy_null(args_v1.name, path); - ret = ioctl(r->dest_dir_fd, BTRFS_IOC_SUBVOL_CREATE, &args_v1); + ret = ioctl(rctx->dest_dir_fd, BTRFS_IOC_SUBVOL_CREATE, &args_v1); if (ret < 0) { ret = -errno; error("creating subvolume %s failed: %s", path, strerror(-ret)); @@ -215,29 +227,40 @@ static int process_snapshot(const char *path, const u8 *uuid, u64 ctransid, void *user) { int ret; - struct btrfs_receive *r = user; + struct btrfs_receive *rctx = user; char uuid_str[BTRFS_UUID_UNPARSED_SIZE]; struct btrfs_ioctl_vol_args_v2 args_v2; struct subvol_info *parent_subvol = NULL; - ret = finish_subvol(r); + ret = finish_subvol(rctx); if (ret < 0) goto out; - BUG_ON(r->cur_subvol.path); - BUG_ON(r->cur_subvol_path[0]); + if (rctx->cur_subvol.path) { + error("snapshot: another one already started, path ptr: %s", + rctx->cur_subvol.path); + ret = -EINVAL; + goto out; + } + if (rctx->cur_subvol_path[0]) { + error("snapshot: another one already started, path buf: %s", + rctx->cur_subvol.path); + ret = -EINVAL; + goto out; + } - if (*r->dest_dir_path == 0) { - strncpy_null(r->cur_subvol_path, path); + if (*rctx->dest_dir_path == 0) { + strncpy_null(rctx->cur_subvol_path, path); } else { - ret = path_cat_out(r->cur_subvol_path, r->dest_dir_path, path); + ret = path_cat_out(rctx->cur_subvol_path, rctx->dest_dir_path, + path); if (ret < 0) { error("snapshot: path invalid: %s", path); goto out; } } - ret = path_cat3_out(r->full_subvol_path, r->root_path, - r->dest_dir_path, path); + ret = path_cat3_out(rctx->full_subvol_path, rctx->root_path, + rctx->dest_dir_path, path); if (ret < 0) { error("snapshot: path invalid: %s", path); goto out; @@ -245,14 +268,14 @@ static int process_snapshot(const char *path, const u8 *uuid, u64 ctransid, fprintf(stdout, "At snapshot %s\n", path); - memcpy(r->cur_subvol.received_uuid, uuid, BTRFS_UUID_SIZE); - r->cur_subvol.stransid = ctransid; + memcpy(rctx->cur_subvol.received_uuid, uuid, BTRFS_UUID_SIZE); + rctx->cur_subvol.stransid = ctransid; if (g_verbose) { - uuid_unparse((u8*)r->cur_subvol.received_uuid, uuid_str); + uuid_unparse((u8*)rctx->cur_subvol.received_uuid, uuid_str); fprintf(stderr, "receiving snapshot %s uuid=%s, " "ctransid=%llu ", path, uuid_str, - r->cur_subvol.stransid); + rctx->cur_subvol.stransid); uuid_unparse(parent_uuid, uuid_str); fprintf(stderr, "parent_uuid=%s, parent_ctransid=%llu\n", uuid_str, parent_ctransid); @@ -261,14 +284,19 @@ static int process_snapshot(const char *path, const u8 *uuid, u64 ctransid, memset(&args_v2, 0, sizeof(args_v2)); strncpy_null(args_v2.name, path); - parent_subvol = subvol_uuid_search(&r->sus, 0, parent_uuid, - parent_ctransid, NULL, subvol_search_by_received_uuid); - if (!parent_subvol) { - parent_subvol = subvol_uuid_search(&r->sus, 0, parent_uuid, - parent_ctransid, NULL, subvol_search_by_uuid); + parent_subvol = subvol_uuid_search(&rctx->sus, 0, parent_uuid, + parent_ctransid, NULL, + subvol_search_by_received_uuid); + if (IS_ERR_OR_NULL(parent_subvol)) { + parent_subvol = subvol_uuid_search(&rctx->sus, 0, parent_uuid, + parent_ctransid, NULL, + subvol_search_by_uuid); } - if (!parent_subvol) { - ret = -ENOENT; + if (IS_ERR_OR_NULL(parent_subvol)) { + if (!parent_subvol) + ret = -ENOENT; + else + ret = PTR_ERR(parent_subvol); error("cannot find parent subvolume"); goto out; } @@ -278,16 +306,16 @@ static int process_snapshot(const char *path, const u8 *uuid, u64 ctransid, * subvolume under the root subvolume, so try and adjust the path to be * relative to our root path. */ - if (r->full_root_path) { + if (rctx->full_root_path) { size_t root_len; size_t sub_len; - root_len = strlen(r->full_root_path); + root_len = strlen(rctx->full_root_path); sub_len = strlen(parent_subvol->path); /* First make sure the parent subvol is actually in our path */ if (sub_len < root_len || - strstr(parent_subvol->path, r->full_root_path) == NULL) { + strstr(parent_subvol->path, rctx->full_root_path) == NULL) { error( "parent subvol is not reachable from inside the root subvol"); ret = -ENOENT; @@ -324,10 +352,10 @@ static int process_snapshot(const char *path, const u8 *uuid, u64 ctransid, }*/ if (*parent_subvol->path == 0) - args_v2.fd = dup(r->mnt_fd); + args_v2.fd = dup(rctx->mnt_fd); else - args_v2.fd = openat(r->mnt_fd, parent_subvol->path, - O_RDONLY | O_NOATIME); + args_v2.fd = openat(rctx->mnt_fd, parent_subvol->path, + O_RDONLY | O_NOATIME); if (args_v2.fd < 0) { ret = -errno; if (errno != ENOENT) @@ -342,7 +370,7 @@ static int process_snapshot(const char *path, const u8 *uuid, u64 ctransid, goto out; } - ret = ioctl(r->dest_dir_fd, BTRFS_IOC_SNAP_CREATE_V2, &args_v2); + ret = ioctl(rctx->dest_dir_fd, BTRFS_IOC_SNAP_CREATE_V2, &args_v2); close(args_v2.fd); if (ret < 0) { ret = -errno; @@ -362,10 +390,10 @@ out: static int process_mkfile(const char *path, void *user) { int ret; - struct btrfs_receive *r = user; + struct btrfs_receive *rctx = user; char full_path[PATH_MAX]; - ret = path_cat_out(full_path, r->full_subvol_path, path); + ret = path_cat_out(full_path, rctx->full_subvol_path, path); if (ret < 0) { error("mkfile: path invalid: %s", path); goto out; @@ -390,10 +418,10 @@ out: static int process_mkdir(const char *path, void *user) { int ret; - struct btrfs_receive *r = user; + struct btrfs_receive *rctx = user; char full_path[PATH_MAX]; - ret = path_cat_out(full_path, r->full_subvol_path, path); + ret = path_cat_out(full_path, rctx->full_subvol_path, path); if (ret < 0) { error("mkdir: path invalid: %s", path); goto out; @@ -415,10 +443,10 @@ out: static int process_mknod(const char *path, u64 mode, u64 dev, void *user) { int ret; - struct btrfs_receive *r = user; + struct btrfs_receive *rctx = user; char full_path[PATH_MAX]; - ret = path_cat_out(full_path, r->full_subvol_path, path); + ret = path_cat_out(full_path, rctx->full_subvol_path, path); if (ret < 0) { error("mknod: path invalid: %s", path); goto out; @@ -441,10 +469,10 @@ out: static int process_mkfifo(const char *path, void *user) { int ret; - struct btrfs_receive *r = user; + struct btrfs_receive *rctx = user; char full_path[PATH_MAX]; - ret = path_cat_out(full_path, r->full_subvol_path, path); + ret = path_cat_out(full_path, rctx->full_subvol_path, path); if (ret < 0) { error("mkfifo: path invalid: %s", path); goto out; @@ -466,10 +494,10 @@ out: static int process_mksock(const char *path, void *user) { int ret; - struct btrfs_receive *r = user; + struct btrfs_receive *rctx = user; char full_path[PATH_MAX]; - ret = path_cat_out(full_path, r->full_subvol_path, path); + ret = path_cat_out(full_path, rctx->full_subvol_path, path); if (ret < 0) { error("mksock: path invalid: %s", path); goto out; @@ -491,10 +519,10 @@ out: static int process_symlink(const char *path, const char *lnk, void *user) { int ret; - struct btrfs_receive *r = user; + struct btrfs_receive *rctx = user; char full_path[PATH_MAX]; - ret = path_cat_out(full_path, r->full_subvol_path, path); + ret = path_cat_out(full_path, rctx->full_subvol_path, path); if (ret < 0) { error("symlink: path invalid: %s", path); goto out; @@ -517,17 +545,17 @@ out: static int process_rename(const char *from, const char *to, void *user) { int ret; - struct btrfs_receive *r = user; + struct btrfs_receive *rctx = user; char full_from[PATH_MAX]; char full_to[PATH_MAX]; - ret = path_cat_out(full_from, r->full_subvol_path, from); + ret = path_cat_out(full_from, rctx->full_subvol_path, from); if (ret < 0) { error("rename: source path invalid: %s", from); goto out; } - ret = path_cat_out(full_to, r->full_subvol_path, to); + ret = path_cat_out(full_to, rctx->full_subvol_path, to); if (ret < 0) { error("rename: target path invalid: %s", to); goto out; @@ -550,17 +578,17 @@ out: static int process_link(const char *path, const char *lnk, void *user) { int ret; - struct btrfs_receive *r = user; + struct btrfs_receive *rctx = user; char full_path[PATH_MAX]; char full_link_path[PATH_MAX]; - ret = path_cat_out(full_path, r->full_subvol_path, path); + ret = path_cat_out(full_path, rctx->full_subvol_path, path); if (ret < 0) { error("link: source path invalid: %s", full_path); goto out; } - ret = path_cat_out(full_link_path, r->full_subvol_path, lnk); + ret = path_cat_out(full_link_path, rctx->full_subvol_path, lnk); if (ret < 0) { error("link: target path invalid: %s", full_link_path); goto out; @@ -583,10 +611,10 @@ out: static int process_unlink(const char *path, void *user) { int ret; - struct btrfs_receive *r = user; + struct btrfs_receive *rctx = user; char full_path[PATH_MAX]; - ret = path_cat_out(full_path, r->full_subvol_path, path); + ret = path_cat_out(full_path, rctx->full_subvol_path, path); if (ret < 0) { error("unlink: path invalid: %s", path); goto out; @@ -608,10 +636,10 @@ out: static int process_rmdir(const char *path, void *user) { int ret; - struct btrfs_receive *r = user; + struct btrfs_receive *rctx = user; char full_path[PATH_MAX]; - ret = path_cat_out(full_path, r->full_subvol_path, path); + ret = path_cat_out(full_path, rctx->full_subvol_path, path); if (ret < 0) { error("rmdir: path invalid: %s", path); goto out; @@ -630,64 +658,64 @@ out: return ret; } -static int open_inode_for_write(struct btrfs_receive *r, const char *path) +static int open_inode_for_write(struct btrfs_receive *rctx, const char *path) { int ret = 0; - if (r->write_fd != -1) { - if (strcmp(r->write_path, path) == 0) + if (rctx->write_fd != -1) { + if (strcmp(rctx->write_path, path) == 0) goto out; - close(r->write_fd); - r->write_fd = -1; + close(rctx->write_fd); + rctx->write_fd = -1; } - r->write_fd = open(path, O_RDWR); - if (r->write_fd < 0) { + rctx->write_fd = open(path, O_RDWR); + if (rctx->write_fd < 0) { ret = -errno; error("cannot open %s: %s", path, strerror(-ret)); goto out; } - strncpy_null(r->write_path, path); + strncpy_null(rctx->write_path, path); out: return ret; } -static void close_inode_for_write(struct btrfs_receive *r) +static void close_inode_for_write(struct btrfs_receive *rctx) { - if(r->write_fd == -1) + if(rctx->write_fd == -1) return; - close(r->write_fd); - r->write_fd = -1; - r->write_path[0] = 0; + close(rctx->write_fd); + rctx->write_fd = -1; + rctx->write_path[0] = 0; } static int process_write(const char *path, const void *data, u64 offset, u64 len, void *user) { int ret = 0; - struct btrfs_receive *r = user; + struct btrfs_receive *rctx = user; char full_path[PATH_MAX]; u64 pos = 0; int w; - ret = path_cat_out(full_path, r->full_subvol_path, path); + ret = path_cat_out(full_path, rctx->full_subvol_path, path); if (ret < 0) { error("write: path invalid: %s", path); goto out; } - ret = open_inode_for_write(r, full_path); + ret = open_inode_for_write(rctx, full_path); if (ret < 0) goto out; while (pos < len) { - w = pwrite(r->write_fd, (char*)data + pos, len - pos, + w = pwrite(rctx->write_fd, (char*)data + pos, len - pos, offset + pos); if (w < 0) { ret = -errno; - error("writing to %s failed: %s\n", + error("writing to %s failed: %s", path, strerror(-ret)); goto out; } @@ -704,7 +732,7 @@ static int process_clone(const char *path, u64 offset, u64 len, void *user) { int ret; - struct btrfs_receive *r = user; + struct btrfs_receive *rctx = user; struct btrfs_ioctl_clone_range_args clone_args; struct subvol_info *si = NULL; char full_path[PATH_MAX]; @@ -712,25 +740,29 @@ static int process_clone(const char *path, u64 offset, u64 len, char full_clone_path[PATH_MAX]; int clone_fd = -1; - ret = path_cat_out(full_path, r->full_subvol_path, path); + ret = path_cat_out(full_path, rctx->full_subvol_path, path); if (ret < 0) { error("clone: source path invalid: %s", path); goto out; } - ret = open_inode_for_write(r, full_path); + ret = open_inode_for_write(rctx, full_path); if (ret < 0) goto out; - si = subvol_uuid_search(&r->sus, 0, clone_uuid, clone_ctransid, NULL, - subvol_search_by_received_uuid); - if (!si) { - if (memcmp(clone_uuid, r->cur_subvol.received_uuid, + si = subvol_uuid_search(&rctx->sus, 0, clone_uuid, clone_ctransid, + NULL, + subvol_search_by_received_uuid); + if (IS_ERR_OR_NULL(si)) { + if (memcmp(clone_uuid, rctx->cur_subvol.received_uuid, BTRFS_UUID_SIZE) == 0) { /* TODO check generation of extent */ - subvol_path = strdup(r->cur_subvol_path); + subvol_path = strdup(rctx->cur_subvol_path); } else { - ret = -ENOENT; + if (!si) + ret = -ENOENT; + else + ret = PTR_ERR(si); error("clone: did not find source subvol"); goto out; } @@ -759,7 +791,7 @@ static int process_clone(const char *path, u64 offset, u64 len, goto out; } - clone_fd = openat(r->mnt_fd, full_clone_path, O_RDONLY | O_NOATIME); + clone_fd = openat(rctx->mnt_fd, full_clone_path, O_RDONLY | O_NOATIME); if (clone_fd < 0) { ret = -errno; error("cannot open %s: %s", full_clone_path, strerror(-ret)); @@ -770,10 +802,10 @@ static int process_clone(const char *path, u64 offset, u64 len, clone_args.src_offset = clone_offset; clone_args.src_length = len; clone_args.dest_offset = offset; - ret = ioctl(r->write_fd, BTRFS_IOC_CLONE_RANGE, &clone_args); + ret = ioctl(rctx->write_fd, BTRFS_IOC_CLONE_RANGE, &clone_args); if (ret < 0) { ret = -errno; - error("failed to clone extents to %s\n%s\n", + error("failed to clone extents to %s\n%s", path, strerror(-ret)); goto out; } @@ -794,10 +826,10 @@ static int process_set_xattr(const char *path, const char *name, const void *data, int len, void *user) { int ret = 0; - struct btrfs_receive *r = user; + struct btrfs_receive *rctx = user; char full_path[PATH_MAX]; - ret = path_cat_out(full_path, r->full_subvol_path, path); + ret = path_cat_out(full_path, rctx->full_subvol_path, path); if (ret < 0) { error("set_xattr: path invalid: %s", path); goto out; @@ -806,17 +838,17 @@ static int process_set_xattr(const char *path, const char *name, if (strcmp("security.capability", name) == 0) { if (g_verbose >= 3) fprintf(stderr, "set_xattr: cache capabilities\n"); - if (r->cached_capabilities_len) + if (rctx->cached_capabilities_len) warning("capabilities set multiple times per file: %s", full_path); - if (len > sizeof(r->cached_capabilities)) { + if (len > sizeof(rctx->cached_capabilities)) { error("capabilities encoded to %d bytes, buffer too small", len); ret = -E2BIG; goto out; } - r->cached_capabilities_len = len; - memcpy(r->cached_capabilities, data, len); + rctx->cached_capabilities_len = len; + memcpy(rctx->cached_capabilities, data, len); } if (g_verbose >= 2) { @@ -840,10 +872,10 @@ out: static int process_remove_xattr(const char *path, const char *name, void *user) { int ret = 0; - struct btrfs_receive *r = user; + struct btrfs_receive *rctx = user; char full_path[PATH_MAX]; - ret = path_cat_out(full_path, r->full_subvol_path, path); + ret = path_cat_out(full_path, rctx->full_subvol_path, path); if (ret < 0) { error("remove_xattr: path invalid: %s", path); goto out; @@ -869,10 +901,10 @@ out: static int process_truncate(const char *path, u64 size, void *user) { int ret = 0; - struct btrfs_receive *r = user; + struct btrfs_receive *rctx = user; char full_path[PATH_MAX]; - ret = path_cat_out(full_path, r->full_subvol_path, path); + ret = path_cat_out(full_path, rctx->full_subvol_path, path); if (ret < 0) { error("truncate: path invalid: %s", path); goto out; @@ -895,10 +927,10 @@ out: static int process_chmod(const char *path, u64 mode, void *user) { int ret = 0; - struct btrfs_receive *r = user; + struct btrfs_receive *rctx = user; char full_path[PATH_MAX]; - ret = path_cat_out(full_path, r->full_subvol_path, path); + ret = path_cat_out(full_path, rctx->full_subvol_path, path); if (ret < 0) { error("chmod: path invalid: %s", path); goto out; @@ -921,10 +953,10 @@ out: static int process_chown(const char *path, u64 uid, u64 gid, void *user) { int ret = 0; - struct btrfs_receive *r = user; + struct btrfs_receive *rctx = user; char full_path[PATH_MAX]; - ret = path_cat_out(full_path, r->full_subvol_path, path); + ret = path_cat_out(full_path, rctx->full_subvol_path, path); if (ret < 0) { error("chown: path invalid: %s", path); goto out; @@ -941,15 +973,15 @@ static int process_chown(const char *path, u64 uid, u64 gid, void *user) goto out; } - if (r->cached_capabilities_len) { + if (rctx->cached_capabilities_len) { if (g_verbose >= 2) fprintf(stderr, "chown: restore capabilities\n"); ret = lsetxattr(full_path, "security.capability", - r->cached_capabilities, - r->cached_capabilities_len, 0); - memset(r->cached_capabilities, 0, - sizeof(r->cached_capabilities)); - r->cached_capabilities_len = 0; + rctx->cached_capabilities, + rctx->cached_capabilities_len, 0); + memset(rctx->cached_capabilities, 0, + sizeof(rctx->cached_capabilities)); + rctx->cached_capabilities_len = 0; if (ret < 0) { ret = -errno; error("restoring capabilities %s: %s", @@ -967,11 +999,11 @@ static int process_utimes(const char *path, struct timespec *at, void *user) { int ret = 0; - struct btrfs_receive *r = user; + struct btrfs_receive *rctx = user; char full_path[PATH_MAX]; struct timespec tv[2]; - ret = path_cat_out(full_path, r->full_subvol_path, path); + ret = path_cat_out(full_path, rctx->full_subvol_path, path); if (ret < 0) { error("utimes: path invalid: %s", path); goto out; @@ -1033,7 +1065,7 @@ static struct btrfs_send_ops send_ops = { .update_extent = process_update_extent, }; -static int do_receive(struct btrfs_receive *r, const char *tomnt, +static int do_receive(struct btrfs_receive *rctx, const char *tomnt, char *realmnt, int r_fd, u64 max_errors) { u64 subvol_id; @@ -1041,6 +1073,7 @@ static int do_receive(struct btrfs_receive *r, const char *tomnt, char *dest_dir_full_path; char root_subvol_path[PATH_MAX]; int end = 0; + int count; dest_dir_full_path = realpath(tomnt, NULL); if (!dest_dir_full_path) { @@ -1048,8 +1081,8 @@ static int do_receive(struct btrfs_receive *r, const char *tomnt, error("realpath(%s) failed: %s", tomnt, strerror(-ret)); goto out; } - r->dest_dir_fd = open(dest_dir_full_path, O_RDONLY | O_NOATIME); - if (r->dest_dir_fd < 0) { + rctx->dest_dir_fd = open(dest_dir_full_path, O_RDONLY | O_NOATIME); + if (rctx->dest_dir_fd < 0) { ret = -errno; error("cannot open destination directory %s: %s", dest_dir_full_path, strerror(-ret)); @@ -1057,9 +1090,9 @@ static int do_receive(struct btrfs_receive *r, const char *tomnt, } if (realmnt[0]) { - r->root_path = realmnt; + rctx->root_path = realmnt; } else { - ret = find_mount_root(dest_dir_full_path, &r->root_path); + ret = find_mount_root(dest_dir_full_path, &rctx->root_path); if (ret < 0) { error("failed to determine mount point for %s: %s", dest_dir_full_path, strerror(-ret)); @@ -1073,10 +1106,10 @@ static int do_receive(struct btrfs_receive *r, const char *tomnt, goto out; } } - r->mnt_fd = open(r->root_path, O_RDONLY | O_NOATIME); - if (r->mnt_fd < 0) { + rctx->mnt_fd = open(rctx->root_path, O_RDONLY | O_NOATIME); + if (rctx->mnt_fd < 0) { ret = -errno; - error("cannot open %s: %s", r->root_path, strerror(-ret)); + error("cannot open %s: %s", rctx->root_path, strerror(-ret)); goto out; } @@ -1085,15 +1118,12 @@ static int do_receive(struct btrfs_receive *r, const char *tomnt, * subvolume we're sitting in so that we can adjust the paths of any * subvols we want to receive in. */ - ret = btrfs_list_get_path_rootid(r->mnt_fd, &subvol_id); - if (ret) { - error("cannot resolve our subvolid: %d", - ret); + ret = btrfs_list_get_path_rootid(rctx->mnt_fd, &subvol_id); + if (ret) goto out; - } root_subvol_path[0] = 0; - ret = btrfs_subvolid_resolve(r->mnt_fd, root_subvol_path, + ret = btrfs_subvolid_resolve(rctx->mnt_fd, root_subvol_path, PATH_MAX, subvol_id); if (ret) { error("cannot resolve our subvol path"); @@ -1105,9 +1135,9 @@ static int do_receive(struct btrfs_receive *r, const char *tomnt, * actually set full_root_path. */ if (*root_subvol_path) - r->full_root_path = root_subvol_path; + rctx->full_root_path = root_subvol_path; - if (r->dest_dir_chroot) { + if (rctx->dest_dir_chroot) { if (chroot(dest_dir_full_path)) { ret = -errno; error("failed to chroot to %s: %s", @@ -1121,66 +1151,75 @@ static int do_receive(struct btrfs_receive *r, const char *tomnt, goto out; } fprintf(stderr, "Chroot to %s\n", dest_dir_full_path); - r->root_path = strdup("/"); - r->dest_dir_path = r->root_path; + rctx->root_path = strdup("/"); + rctx->dest_dir_path = rctx->root_path; } else { /* * find_mount_root returns a root_path that is a subpath of * dest_dir_full_path. Now get the other part of root_path, * which is the destination dir relative to root_path. */ - r->dest_dir_path = dest_dir_full_path + strlen(r->root_path); - while (r->dest_dir_path[0] == '/') - r->dest_dir_path++; + rctx->dest_dir_path = dest_dir_full_path + strlen(rctx->root_path); + while (rctx->dest_dir_path[0] == '/') + rctx->dest_dir_path++; } - ret = subvol_uuid_search_init(r->mnt_fd, &r->sus); + ret = subvol_uuid_search_init(rctx->mnt_fd, &rctx->sus); if (ret < 0) goto out; + count = 0; while (!end) { - if (r->cached_capabilities_len) { + if (rctx->cached_capabilities_len) { if (g_verbose >= 3) fprintf(stderr, "clear cached capabilities\n"); - memset(r->cached_capabilities, 0, - sizeof(r->cached_capabilities)); - r->cached_capabilities_len = 0; + memset(rctx->cached_capabilities, 0, + sizeof(rctx->cached_capabilities)); + rctx->cached_capabilities_len = 0; } - ret = btrfs_read_and_process_send_stream(r_fd, &send_ops, r, - r->honor_end_cmd, + ret = btrfs_read_and_process_send_stream(r_fd, &send_ops, + rctx, + rctx->honor_end_cmd, max_errors); if (ret < 0) goto out; + /* Empty stream is invalid */ + if (ret && count == 0) { + error("empty stream is not considered valid"); + ret = -EINVAL; + goto out; + } + count++; if (ret) end = 1; - close_inode_for_write(r); - ret = finish_subvol(r); + close_inode_for_write(rctx); + ret = finish_subvol(rctx); if (ret < 0) goto out; } ret = 0; out: - if (r->write_fd != -1) { - close(r->write_fd); - r->write_fd = -1; + if (rctx->write_fd != -1) { + close(rctx->write_fd); + rctx->write_fd = -1; } - if (r->root_path != realmnt) - free(r->root_path); - r->root_path = NULL; - r->dest_dir_path = NULL; + if (rctx->root_path != realmnt) + free(rctx->root_path); + rctx->root_path = NULL; + rctx->dest_dir_path = NULL; free(dest_dir_full_path); - subvol_uuid_search_finit(&r->sus); - if (r->mnt_fd != -1) { - close(r->mnt_fd); - r->mnt_fd = -1; + subvol_uuid_search_finit(&rctx->sus); + if (rctx->mnt_fd != -1) { + close(rctx->mnt_fd); + rctx->mnt_fd = -1; } - if (r->dest_dir_fd != -1) { - close(r->dest_dir_fd); - r->dest_dir_fd = -1; + if (rctx->dest_dir_fd != -1) { + close(rctx->dest_dir_fd); + rctx->dest_dir_fd = -1; } return ret; @@ -1191,24 +1230,27 @@ int cmd_receive(int argc, char **argv) char *tomnt = NULL; char fromfile[PATH_MAX]; char realmnt[PATH_MAX]; - struct btrfs_receive r; + struct btrfs_receive rctx; int receive_fd = fileno(stdin); u64 max_errors = 1; + int dump = 0; int ret = 0; - memset(&r, 0, sizeof(r)); - r.mnt_fd = -1; - r.write_fd = -1; - r.dest_dir_fd = -1; - r.dest_dir_chroot = 0; + memset(&rctx, 0, sizeof(rctx)); + rctx.mnt_fd = -1; + rctx.write_fd = -1; + rctx.dest_dir_fd = -1; + rctx.dest_dir_chroot = 0; realmnt[0] = 0; fromfile[0] = 0; while (1) { int c; + enum { GETOPT_VAL_DUMP = 257 }; static const struct option long_opts[] = { { "max-errors", required_argument, NULL, 'E' }, { "chroot", no_argument, NULL, 'C' }, + { "dump", no_argument, NULL, GETOPT_VAL_DUMP }, { NULL, 0, NULL, 0 } }; @@ -1229,10 +1271,10 @@ int cmd_receive(int argc, char **argv) } break; case 'e': - r.honor_end_cmd = 1; + rctx.honor_end_cmd = 1; break; case 'C': - r.dest_dir_chroot = 1; + rctx.dest_dir_chroot = 1; break; case 'E': max_errors = arg_strtou64(optarg); @@ -1245,6 +1287,9 @@ int cmd_receive(int argc, char **argv) goto out; } break; + case GETOPT_VAL_DUMP: + dump = 1; + break; case '?': default: error("receive args invalid"); @@ -1252,7 +1297,9 @@ int cmd_receive(int argc, char **argv) } } - if (check_argc_exact(argc - optind, 1)) + if (dump && check_argc_exact(argc - optind, 0)) + usage(cmd_receive_usage); + if (!dump && check_argc_exact(argc - optind, 1)) usage(cmd_receive_usage); tomnt = argv[optind]; @@ -1265,42 +1312,56 @@ int cmd_receive(int argc, char **argv) } } - ret = do_receive(&r, tomnt, realmnt, receive_fd, max_errors); + if (dump) { + struct btrfs_dump_send_args dump_args; + + dump_args.root_path[0] = '.'; + dump_args.root_path[1] = '\0'; + dump_args.full_subvol_path[0] = '.'; + dump_args.full_subvol_path[1] = '\0'; + ret = btrfs_read_and_process_send_stream(receive_fd, + &btrfs_print_send_ops, &dump_args, 0, 0); + if (ret < 0) + error("failed to dump the send stream: %s", + strerror(-ret)); + } else { + ret = do_receive(&rctx, tomnt, realmnt, receive_fd, max_errors); + } + if (receive_fd != fileno(stdin)) close(receive_fd); - out: return !!ret; } const char * const cmd_receive_usage[] = { - "btrfs receive [-ve] [-f <infile>] [--max-errors <N>] <mount>", - "Receive subvolumes from stdin.", + "btrfs receive [options] <mount>\n" + "btrfs receive --dump [options]", + "Receive subvolumes from a stream", "Receives one or more subvolumes that were previously", "sent with btrfs send. The received subvolumes are stored", - "into <mount>.", - "btrfs receive will fail in case a receiving subvolume", + "into MOUNT.", + "The receive will fail in case the receiving subvolume", "already exists. It will also fail in case a previously", - "received subvolume was changed after it was received.", + "received subvolume has been changed after it was received.", "After receiving a subvolume, it is immediately set to", - "read only.\n", - "-v Enable verbose debug output. Each", - " occurrence of this option increases the", - " verbose level more.", - "-f <infile> By default, btrfs receive uses stdin", - " to receive the subvolumes. Use this", - " option to specify a file to use instead.", - "-e Terminate after receiving an <end cmd>", - " in the data stream. Without this option,", - " the receiver terminates only if an error", - " is recognized or on EOF.", + "read-only.", + "", + "-v increase verbosity about performed actions", + "-f FILE read the stream from FILE instead of stdin", + "-e terminate after receiving an <end cmd> marker in the stream.", + " Without this option the receiver side terminates only in case", + " of an error on end of file.", "-C|--chroot confine the process to <mount> using chroot", - "--max-errors <N> Terminate as soon as N errors happened while", - " processing commands from the send stream.", + "-E|--max-errors NERR", + " terminate as soon as NERR errors occur while", + " stream processing commands from the stream.", " Default value is 1. A value of 0 means no limit.", - "-m <mountpoint> The root mount point of the destination fs.", - " If you do not have /proc use this to tell us where ", + "-m ROOTMOUNT the root mount point of the destination filesystem.", + " If /proc is not accessible, use this to tell us where", " this file system is mounted.", + "--dump dump stream metadata, one line per operation,", + " does not require the MOUNT parameter", NULL }; diff --git a/cmds-replace.c b/cmds-replace.c index d1bf057e..9345da23 100644 --- a/cmds-replace.c +++ b/cmds-replace.c @@ -436,7 +436,7 @@ static int print_replace_status(int fd, const char *path, int once) printf("Never started"); break; default: - error("unknown status from ioctl DEV_REPLACE_STATUS on '%s': %llu\n", + error("unknown status from ioctl DEV_REPLACE_STATUS on '%s': %llu", path, status->replace_state); return -EINVAL; } diff --git a/cmds-restore.c b/cmds-restore.c index b491f083..bdd35bd7 100644 --- a/cmds-restore.c +++ b/cmds-restore.c @@ -330,7 +330,7 @@ static int copy_one_extent(struct btrfs_root *root, int fd, inbuf = malloc(size_left); if (!inbuf) { - error("not enough memory\n"); + error("not enough memory"); return -ENOMEM; } @@ -356,7 +356,7 @@ again: dev_fd = device->fd; device->total_ios++; dev_bytenr = multi->stripes[0].physical; - kfree(multi); + free(multi); if (size_left < length) length = size_left; @@ -461,7 +461,7 @@ static int set_file_xattrs(struct btrfs_root *root, u64 inode, int fd, const char *file_name) { struct btrfs_key key; - struct btrfs_path *path; + struct btrfs_path path; struct extent_buffer *leaf; struct btrfs_dir_item *di; u32 name_len = 0; @@ -472,25 +472,21 @@ static int set_file_xattrs(struct btrfs_root *root, u64 inode, char *data = NULL; int ret = 0; + btrfs_init_path(&path); key.objectid = inode; key.type = BTRFS_XATTR_ITEM_KEY; key.offset = 0; - - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - - ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); + ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0); if (ret < 0) goto out; - leaf = path->nodes[0]; + leaf = path.nodes[0]; while (1) { - if (path->slots[0] >= btrfs_header_nritems(leaf)) { + if (path.slots[0] >= btrfs_header_nritems(leaf)) { do { - ret = next_leaf(root, path); + ret = next_leaf(root, &path); if (ret < 0) { - error("searching for extended attributes: %d\n", + error("searching for extended attributes: %d", ret); goto out; } else if (ret) { @@ -498,17 +494,17 @@ static int set_file_xattrs(struct btrfs_root *root, u64 inode, ret = 0; goto out; } - leaf = path->nodes[0]; + leaf = path.nodes[0]; } while (!leaf); continue; } - btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); + btrfs_item_key_to_cpu(leaf, &key, path.slots[0]); if (key.type != BTRFS_XATTR_ITEM_KEY || key.objectid != inode) break; cur = 0; - total_len = btrfs_item_size_nr(leaf, path->slots[0]); - di = btrfs_item_ptr(leaf, path->slots[0], + total_len = btrfs_item_size_nr(leaf, path.slots[0]); + di = btrfs_item_ptr(leaf, path.slots[0], struct btrfs_dir_item); while (cur < total_len) { @@ -548,11 +544,11 @@ static int set_file_xattrs(struct btrfs_root *root, u64 inode, cur += len; di = (struct btrfs_dir_item *)((char *)di + len); } - path->slots[0]++; + path.slots[0]++; } ret = 0; out: - btrfs_free_path(path); + btrfs_release_path(&path); free(name); free(data); @@ -562,44 +558,39 @@ out: static int copy_metadata(struct btrfs_root *root, int fd, struct btrfs_key *key) { - struct btrfs_path *path; + struct btrfs_path path; struct btrfs_inode_item *inode_item; int ret; - path = btrfs_alloc_path(); - if (!path) { - error("not enough memory"); - return -ENOMEM; - } - - ret = btrfs_lookup_inode(NULL, root, path, key, 0); + btrfs_init_path(&path); + ret = btrfs_lookup_inode(NULL, root, &path, key, 0); if (ret == 0) { struct btrfs_timespec *bts; struct timespec times[2]; - inode_item = btrfs_item_ptr(path->nodes[0], path->slots[0], + inode_item = btrfs_item_ptr(path.nodes[0], path.slots[0], struct btrfs_inode_item); - ret = fchown(fd, btrfs_inode_uid(path->nodes[0], inode_item), - btrfs_inode_gid(path->nodes[0], inode_item)); + ret = fchown(fd, btrfs_inode_uid(path.nodes[0], inode_item), + btrfs_inode_gid(path.nodes[0], inode_item)); if (ret) { error("failed to change owner: %s", strerror(errno)); goto out; } - ret = fchmod(fd, btrfs_inode_mode(path->nodes[0], inode_item)); + ret = fchmod(fd, btrfs_inode_mode(path.nodes[0], inode_item)); if (ret) { error("failed to change mode: %s", strerror(errno)); goto out; } bts = btrfs_inode_atime(inode_item); - times[0].tv_sec = btrfs_timespec_sec(path->nodes[0], bts); - times[0].tv_nsec = btrfs_timespec_nsec(path->nodes[0], bts); + times[0].tv_sec = btrfs_timespec_sec(path.nodes[0], bts); + times[0].tv_nsec = btrfs_timespec_nsec(path.nodes[0], bts); bts = btrfs_inode_mtime(inode_item); - times[1].tv_sec = btrfs_timespec_sec(path->nodes[0], bts); - times[1].tv_nsec = btrfs_timespec_nsec(path->nodes[0], bts); + times[1].tv_sec = btrfs_timespec_sec(path.nodes[0], bts); + times[1].tv_nsec = btrfs_timespec_nsec(path.nodes[0], bts); ret = futimens(fd, times); if (ret) { @@ -608,7 +599,7 @@ static int copy_metadata(struct btrfs_root *root, int fd, } } out: - btrfs_free_path(path); + btrfs_release_path(&path); return ret; } @@ -616,7 +607,7 @@ static int copy_file(struct btrfs_root *root, int fd, struct btrfs_key *key, const char *file) { struct extent_buffer *leaf; - struct btrfs_path *path; + struct btrfs_path path; struct btrfs_file_extent_item *fi; struct btrfs_inode_item *inode_item; struct btrfs_timespec *bts; @@ -629,17 +620,12 @@ static int copy_file(struct btrfs_root *root, int fd, struct btrfs_key *key, struct timespec times[2]; int times_ok = 0; - path = btrfs_alloc_path(); - if (!path) { - error("not enough memory"); - return -ENOMEM; - } - - ret = btrfs_lookup_inode(NULL, root, path, key, 0); + btrfs_init_path(&path); + ret = btrfs_lookup_inode(NULL, root, &path, key, 0); if (ret == 0) { - inode_item = btrfs_item_ptr(path->nodes[0], path->slots[0], + inode_item = btrfs_item_ptr(path.nodes[0], path.slots[0], struct btrfs_inode_item); - found_size = btrfs_inode_size(path->nodes[0], inode_item); + found_size = btrfs_inode_size(path.nodes[0], inode_item); if (restore_metadata) { /* @@ -647,39 +633,39 @@ static int copy_file(struct btrfs_root *root, int fd, struct btrfs_key *key, * copyout is finished. */ - ret = fchown(fd, btrfs_inode_uid(path->nodes[0], inode_item), - btrfs_inode_gid(path->nodes[0], inode_item)); + ret = fchown(fd, btrfs_inode_uid(path.nodes[0], inode_item), + btrfs_inode_gid(path.nodes[0], inode_item)); if (ret && !ignore_errors) goto out; - ret = fchmod(fd, btrfs_inode_mode(path->nodes[0], inode_item)); + ret = fchmod(fd, btrfs_inode_mode(path.nodes[0], inode_item)); if (ret && !ignore_errors) goto out; bts = btrfs_inode_atime(inode_item); - times[0].tv_sec = btrfs_timespec_sec(path->nodes[0], bts); - times[0].tv_nsec = btrfs_timespec_nsec(path->nodes[0], bts); + times[0].tv_sec = btrfs_timespec_sec(path.nodes[0], bts); + times[0].tv_nsec = btrfs_timespec_nsec(path.nodes[0], bts); bts = btrfs_inode_mtime(inode_item); - times[1].tv_sec = btrfs_timespec_sec(path->nodes[0], bts); - times[1].tv_nsec = btrfs_timespec_nsec(path->nodes[0], bts); + times[1].tv_sec = btrfs_timespec_sec(path.nodes[0], bts); + times[1].tv_nsec = btrfs_timespec_nsec(path.nodes[0], bts); times_ok = 1; } } - btrfs_release_path(path); + btrfs_release_path(&path); key->offset = 0; key->type = BTRFS_EXTENT_DATA_KEY; - ret = btrfs_search_slot(NULL, root, key, path, 0, 0); + ret = btrfs_search_slot(NULL, root, key, &path, 0, 0); if (ret < 0) { error("searching extent data returned %d", ret); goto out; } - leaf = path->nodes[0]; + leaf = path.nodes[0]; while (!leaf) { - ret = next_leaf(root, path); + ret = next_leaf(root, &path); if (ret < 0) { error("cannot get next leaf: %d", ret); goto out; @@ -688,7 +674,7 @@ static int copy_file(struct btrfs_root *root, int fd, struct btrfs_key *key, ret = 0; goto out; } - leaf = path->nodes[0]; + leaf = path.nodes[0]; } while (1) { @@ -703,27 +689,27 @@ static int copy_file(struct btrfs_root *root, int fd, struct btrfs_key *key, else if (resp == LOOP_DONTASK) loops = -1; } - if (path->slots[0] >= btrfs_header_nritems(leaf)) { + if (path.slots[0] >= btrfs_header_nritems(leaf)) { do { - ret = next_leaf(root, path); + ret = next_leaf(root, &path); if (ret < 0) { fprintf(stderr, "Error searching %d\n", ret); goto out; } else if (ret) { /* No more leaves to search */ - btrfs_free_path(path); + btrfs_release_path(&path); goto set_size; } - leaf = path->nodes[0]; + leaf = path.nodes[0]; } while (!leaf); continue; } - btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]); + btrfs_item_key_to_cpu(leaf, &found_key, path.slots[0]); if (found_key.objectid != key->objectid) break; if (found_key.type != key->type) break; - fi = btrfs_item_ptr(leaf, path->slots[0], + fi = btrfs_item_ptr(leaf, path.slots[0], struct btrfs_file_extent_item); extent_type = btrfs_file_extent_type(leaf, fi); compression = btrfs_file_extent_compression(leaf, fi); @@ -737,7 +723,7 @@ static int copy_file(struct btrfs_root *root, int fd, struct btrfs_key *key, if (extent_type == BTRFS_FILE_EXTENT_PREALLOC) goto next; if (extent_type == BTRFS_FILE_EXTENT_INLINE) { - ret = copy_one_inline(root, fd, path, found_key.offset); + ret = copy_one_inline(root, fd, &path, found_key.offset); if (ret) goto out; } else if (extent_type == BTRFS_FILE_EXTENT_REG) { @@ -749,10 +735,10 @@ static int copy_file(struct btrfs_root *root, int fd, struct btrfs_key *key, warning("weird extent type %d", extent_type); } next: - path->slots[0]++; + path.slots[0]++; } - btrfs_free_path(path); + btrfs_release_path(&path); set_size: if (found_size) { ret = ftruncate(fd, (loff_t)found_size); @@ -772,7 +758,7 @@ set_size: return 0; out: - btrfs_free_path(path); + btrfs_release_path(&path); return ret; } @@ -809,7 +795,7 @@ static int overwrite_ok(const char * path) static int copy_symlink(struct btrfs_root *root, struct btrfs_key *key, const char *file) { - struct btrfs_path *path; + struct btrfs_path path; struct extent_buffer *leaf; struct btrfs_file_extent_item *extent_item; struct btrfs_inode_item *inode_item; @@ -833,29 +819,25 @@ static int copy_symlink(struct btrfs_root *root, struct btrfs_key *key, } } + btrfs_init_path(&path); key->type = BTRFS_EXTENT_DATA_KEY; key->offset = 0; - - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - - ret = btrfs_search_slot(NULL, root, key, path, 0, 0); + ret = btrfs_search_slot(NULL, root, key, &path, 0, 0); if (ret < 0) goto out; - leaf = path->nodes[0]; + leaf = path.nodes[0]; if (!leaf) { fprintf(stderr, "Error getting leaf for symlink '%s'\n", file); ret = -1; goto out; } - extent_item = btrfs_item_ptr(leaf, path->slots[0], + extent_item = btrfs_item_ptr(leaf, path.slots[0], struct btrfs_file_extent_item); len = btrfs_file_extent_inline_item_len(leaf, - btrfs_item_nr(path->slots[0])); + btrfs_item_nr(path.slots[0])); if (len >= PATH_MAX) { fprintf(stderr, "Symlink '%s' target length %d is longer than PATH_MAX\n", fs_name, len); @@ -890,19 +872,19 @@ static int copy_symlink(struct btrfs_root *root, struct btrfs_key *key, key->type = BTRFS_INODE_ITEM_KEY; key->offset = 0; - btrfs_release_path(path); + btrfs_release_path(&path); - ret = btrfs_lookup_inode(NULL, root, path, key, 0); + ret = btrfs_lookup_inode(NULL, root, &path, key, 0); if (ret) { fprintf(stderr, "Failed to lookup inode for '%s'\n", file); goto out; } - inode_item = btrfs_item_ptr(path->nodes[0], path->slots[0], + inode_item = btrfs_item_ptr(path.nodes[0], path.slots[0], struct btrfs_inode_item); - ret = fchownat(-1, file, btrfs_inode_uid(path->nodes[0], inode_item), - btrfs_inode_gid(path->nodes[0], inode_item), + ret = fchownat(-1, file, btrfs_inode_uid(path.nodes[0], inode_item), + btrfs_inode_gid(path.nodes[0], inode_item), AT_SYMLINK_NOFOLLOW); if (ret) { fprintf(stderr, "Failed to change owner: %s\n", @@ -911,18 +893,18 @@ static int copy_symlink(struct btrfs_root *root, struct btrfs_key *key, } bts = btrfs_inode_atime(inode_item); - times[0].tv_sec = btrfs_timespec_sec(path->nodes[0], bts); - times[0].tv_nsec = btrfs_timespec_nsec(path->nodes[0], bts); + times[0].tv_sec = btrfs_timespec_sec(path.nodes[0], bts); + times[0].tv_nsec = btrfs_timespec_nsec(path.nodes[0], bts); bts = btrfs_inode_mtime(inode_item); - times[1].tv_sec = btrfs_timespec_sec(path->nodes[0], bts); - times[1].tv_nsec = btrfs_timespec_nsec(path->nodes[0], bts); + times[1].tv_sec = btrfs_timespec_sec(path.nodes[0], bts); + times[1].tv_nsec = btrfs_timespec_nsec(path.nodes[0], bts); ret = utimensat(-1, file, times, AT_SYMLINK_NOFOLLOW); if (ret) fprintf(stderr, "Failed to set times: %s\n", strerror(errno)); out: - btrfs_free_path(path); + btrfs_release_path(&path); return ret; } @@ -930,7 +912,7 @@ static int search_dir(struct btrfs_root *root, struct btrfs_key *key, const char *output_rootdir, const char *in_dir, const regex_t *mreg) { - struct btrfs_path *path; + struct btrfs_path path; struct extent_buffer *leaf; struct btrfs_dir_item *dir_item; struct btrfs_key found_key, location; @@ -942,16 +924,10 @@ static int search_dir(struct btrfs_root *root, struct btrfs_key *key, int loops = 0; u8 type; - path = btrfs_alloc_path(); - if (!path) { - fprintf(stderr, "Ran out of memory\n"); - return -ENOMEM; - } - + btrfs_init_path(&path); key->offset = 0; key->type = BTRFS_DIR_INDEX_KEY; - - ret = btrfs_search_slot(NULL, root, key, path, 0, 0); + ret = btrfs_search_slot(NULL, root, key, &path, 0, 0); if (ret < 0) { fprintf(stderr, "Error searching %d\n", ret); goto out; @@ -959,12 +935,12 @@ static int search_dir(struct btrfs_root *root, struct btrfs_key *key, ret = 0; - leaf = path->nodes[0]; + leaf = path.nodes[0]; while (!leaf) { if (verbose > 1) printf("No leaf after search, looking for the next " "leaf\n"); - ret = next_leaf(root, path); + ret = next_leaf(root, &path); if (ret < 0) { fprintf(stderr, "Error getting next leaf %d\n", ret); @@ -977,7 +953,7 @@ static int search_dir(struct btrfs_root *root, struct btrfs_key *key, ret = 0; goto out; } - leaf = path->nodes[0]; + leaf = path.nodes[0]; } while (leaf) { @@ -988,9 +964,9 @@ static int search_dir(struct btrfs_root *root, struct btrfs_key *key, break; } - if (path->slots[0] >= btrfs_header_nritems(leaf)) { + if (path.slots[0] >= btrfs_header_nritems(leaf)) { do { - ret = next_leaf(root, path); + ret = next_leaf(root, &path); if (ret < 0) { fprintf(stderr, "Error searching %d\n", ret); @@ -1004,11 +980,11 @@ static int search_dir(struct btrfs_root *root, struct btrfs_key *key, ret = 0; goto out; } - leaf = path->nodes[0]; + leaf = path.nodes[0]; } while (!leaf); continue; } - btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]); + btrfs_item_key_to_cpu(leaf, &found_key, path.slots[0]); if (found_key.objectid != key->objectid) { if (verbose > 1) printf("Found objectid=%Lu, key=%Lu\n", @@ -1021,7 +997,7 @@ static int search_dir(struct btrfs_root *root, struct btrfs_key *key, found_key.type, key->type); break; } - dir_item = btrfs_item_ptr(leaf, path->slots[0], + dir_item = btrfs_item_ptr(leaf, path.slots[0], struct btrfs_dir_item); name_ptr = (unsigned long)(dir_item + 1); name_len = btrfs_dir_name_len(leaf, dir_item); @@ -1153,12 +1129,12 @@ static int search_dir(struct btrfs_root *root, struct btrfs_key *key, if (ret < 0) { if (ignore_errors) goto next; - btrfs_free_path(path); + btrfs_release_path(&path); return ret; } } next: - path->slots[0]++; + path.slots[0]++; } if (restore_metadata) { @@ -1186,7 +1162,7 @@ next: if (verbose) printf("Done searching %s\n", in_dir); out: - btrfs_free_path(path); + btrfs_release_path(&path); return ret; } @@ -1195,7 +1171,7 @@ static int do_list_roots(struct btrfs_root *root) struct btrfs_key key; struct btrfs_key found_key; struct btrfs_disk_key disk_key; - struct btrfs_path *path; + struct btrfs_path path; struct extent_buffer *leaf; struct btrfs_root_item ri; unsigned long offset; @@ -1203,38 +1179,33 @@ static int do_list_roots(struct btrfs_root *root) int ret; root = root->fs_info->tree_root; - path = btrfs_alloc_path(); - if (!path) { - fprintf(stderr, "Failed to alloc path\n"); - return -ENOMEM; - } + btrfs_init_path(&path); key.offset = 0; key.objectid = 0; key.type = BTRFS_ROOT_ITEM_KEY; - - ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); + ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0); if (ret < 0) { fprintf(stderr, "Failed to do search %d\n", ret); - btrfs_free_path(path); + btrfs_release_path(&path); return -1; } - leaf = path->nodes[0]; + leaf = path.nodes[0]; while (1) { - slot = path->slots[0]; + slot = path.slots[0]; if (slot >= btrfs_header_nritems(leaf)) { - ret = btrfs_next_leaf(root, path); + ret = btrfs_next_leaf(root, &path); if (ret) break; - leaf = path->nodes[0]; - slot = path->slots[0]; + leaf = path.nodes[0]; + slot = path.slots[0]; } btrfs_item_key(leaf, &disk_key, slot); btrfs_disk_key_to_cpu(&found_key, &disk_key); - if (btrfs_key_type(&found_key) != BTRFS_ROOT_ITEM_KEY) { - path->slots[0]++; + if (found_key.type != BTRFS_ROOT_ITEM_KEY) { + path.slots[0]++; continue; } @@ -1244,9 +1215,9 @@ static int do_list_roots(struct btrfs_root *root) btrfs_print_key(&disk_key); printf(" %Lu level %d\n", btrfs_root_bytenr(&ri), btrfs_root_level(&ri)); - path->slots[0]++; + path.slots[0]++; } - btrfs_free_path(path); + btrfs_release_path(&path); return 0; } @@ -1317,36 +1288,30 @@ static struct btrfs_root *open_fs(const char *dev, u64 root_location, static int find_first_dir(struct btrfs_root *root, u64 *objectid) { - struct btrfs_path *path; + struct btrfs_path path; struct btrfs_key found_key; struct btrfs_key key; int ret = -1; int i; + btrfs_init_path(&path); key.objectid = 0; key.type = BTRFS_DIR_INDEX_KEY; key.offset = 0; - - path = btrfs_alloc_path(); - if (!path) { - fprintf(stderr, "Ran out of memory\n"); - return ret; - } - - ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); + ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0); if (ret < 0) { fprintf(stderr, "Error searching %d\n", ret); goto out; } - if (!path->nodes[0]) { + if (!path.nodes[0]) { fprintf(stderr, "No leaf!\n"); goto out; } again: - for (i = path->slots[0]; - i < btrfs_header_nritems(path->nodes[0]); i++) { - btrfs_item_key_to_cpu(path->nodes[0], &found_key, i); + for (i = path.slots[0]; + i < btrfs_header_nritems(path.nodes[0]); i++) { + btrfs_item_key_to_cpu(path.nodes[0], &found_key, i); if (found_key.type != key.type) continue; @@ -1357,7 +1322,7 @@ again: goto out; } do { - ret = next_leaf(root, path); + ret = next_leaf(root, &path); if (ret < 0) { fprintf(stderr, "Error getting next leaf %d\n", ret); @@ -1366,12 +1331,12 @@ again: fprintf(stderr, "No more leaves\n"); goto out; } - } while (!path->nodes[0]); - if (path->nodes[0]) + } while (!path.nodes[0]); + if (path.nodes[0]) goto again; printf("Couldn't find a dir index item\n"); out: - btrfs_free_path(path); + btrfs_release_path(&path); return ret; } @@ -1380,9 +1345,9 @@ const char * const cmd_restore_usage[] = { "Try to restore files from a damaged filesystem (unmounted)", "", "-s|--snapshots get snapshots", - "-x|--xattr get extended attributes", + "-x|--xattr restore extended attributes", "-m|--metadata restore owner, mode and times", - "-S|--symlinks restore symbolic links", + "-S|--symlink restore symbolic links", "-v|--verbose verbose", "-i|--ignore-errors ignore errors", "-o|--overwrite overwrite", @@ -1421,8 +1386,10 @@ int cmd_restore(int argc, char **argv) while (1) { int opt; + enum { GETOPT_VAL_PATH_REGEX = 256 }; static const struct option long_options[] = { - { "path-regex", required_argument, NULL, 256}, + { "path-regex", required_argument, NULL, + GETOPT_VAL_PATH_REGEX }, { "dry-run", no_argument, NULL, 'D'}, { "metadata", no_argument, NULL, 'm'}, { "symlinks", no_argument, NULL, 'S'}, @@ -1495,8 +1462,7 @@ int cmd_restore(int argc, char **argv) case 'c': match_cflags |= REG_ICASE; break; - /* long option without single letter alternative */ - case 256: + case GETOPT_VAL_PATH_REGEX: match_regstr = optarg; break; case 'x': diff --git a/cmds-scrub.c b/cmds-scrub.c index c03bc5fb..2cf7f308 100644 --- a/cmds-scrub.c +++ b/cmds-scrub.c @@ -481,7 +481,10 @@ static struct scrub_file_record **scrub_read_file(int fd, int report_errors) again: old_avail = avail - i; - BUG_ON(old_avail < 0); + if (old_avail < 0) { + error("scrub record file corrupted near byte %d", i); + return ERR_PTR(-EINVAL); + } if (old_avail) memmove(l, l + i, old_avail); avail = read(fd, l + old_avail, sizeof(l) - old_avail); @@ -650,7 +653,9 @@ skip: } while (i < avail); continue; } - BUG(); + error("internal error: unknown parser state %d near byte %d", + state, i); + return ERR_PTR(-EINVAL); } goto again; } diff --git a/cmds-send.c b/cmds-send.c index 74d01287..cec11e6b 100644 --- a/cmds-send.c +++ b/cmds-send.c @@ -44,6 +44,8 @@ #include "send.h" #include "send-utils.h" +#define SEND_BUFFER_SIZE (64 * 1024) + /* * Default is 1 for historical reasons, changing may break scripts that expect * the 'At subvol' message. @@ -62,64 +64,72 @@ struct btrfs_send { struct subvol_uuid_search sus; }; -static int get_root_id(struct btrfs_send *s, const char *path, u64 *root_id) +static int get_root_id(struct btrfs_send *sctx, const char *path, u64 *root_id) { struct subvol_info *si; - si = subvol_uuid_search(&s->sus, 0, NULL, 0, path, + si = subvol_uuid_search(&sctx->sus, 0, NULL, 0, path, subvol_search_by_path); - if (!si) - return -ENOENT; + if (IS_ERR_OR_NULL(si)) { + if (!si) + return -ENOENT; + else + return PTR_ERR(si); + } *root_id = si->root_id; free(si->path); free(si); return 0; } -static struct subvol_info *get_parent(struct btrfs_send *s, u64 root_id) +static struct subvol_info *get_parent(struct btrfs_send *sctx, u64 root_id) { struct subvol_info *si_tmp; struct subvol_info *si; - si_tmp = subvol_uuid_search(&s->sus, root_id, NULL, 0, NULL, + si_tmp = subvol_uuid_search(&sctx->sus, root_id, NULL, 0, NULL, subvol_search_by_root_id); - if (!si_tmp) - return NULL; + if (IS_ERR_OR_NULL(si_tmp)) + return si_tmp; - si = subvol_uuid_search(&s->sus, 0, si_tmp->parent_uuid, 0, NULL, + si = subvol_uuid_search(&sctx->sus, 0, si_tmp->parent_uuid, 0, NULL, subvol_search_by_uuid); free(si_tmp->path); free(si_tmp); return si; } -static int find_good_parent(struct btrfs_send *s, u64 root_id, u64 *found) +static int find_good_parent(struct btrfs_send *sctx, u64 root_id, u64 *found) { int ret; struct subvol_info *parent = NULL; struct subvol_info *parent2 = NULL; struct subvol_info *best_parent = NULL; - __s64 tmp; u64 best_diff = (u64)-1; int i; - parent = get_parent(s, root_id); - if (!parent) { - ret = -ENOENT; + parent = get_parent(sctx, root_id); + if (IS_ERR_OR_NULL(parent)) { + if (!parent) + ret = -ENOENT; + else + ret = PTR_ERR(parent); goto out; } - for (i = 0; i < s->clone_sources_count; i++) { - if (s->clone_sources[i] == parent->root_id) { + for (i = 0; i < sctx->clone_sources_count; i++) { + if (sctx->clone_sources[i] == parent->root_id) { best_parent = parent; parent = NULL; goto out_found; } } - for (i = 0; i < s->clone_sources_count; i++) { - parent2 = get_parent(s, s->clone_sources[i]); - if (!parent2) + for (i = 0; i < sctx->clone_sources_count; i++) { + s64 tmp; + + parent2 = get_parent(sctx, sctx->clone_sources[i]); + if (IS_ERR_OR_NULL(parent2)) continue; if (parent2->root_id != parent->root_id) { free(parent2->path); @@ -130,16 +140,19 @@ static int find_good_parent(struct btrfs_send *s, u64 root_id, u64 *found) free(parent2->path); free(parent2); - parent2 = subvol_uuid_search(&s->sus, s->clone_sources[i], NULL, - 0, NULL, subvol_search_by_root_id); - - if (!parent2) { - ret = -ENOENT; + parent2 = subvol_uuid_search(&sctx->sus, + sctx->clone_sources[i], NULL, 0, NULL, + subvol_search_by_root_id); + if (IS_ERR_OR_NULL(parent2)) { + if (!parent2) + ret = -ENOENT; + else + ret = PTR_ERR(parent2); goto out; } tmp = parent2->ctransid - parent->ctransid; if (tmp < 0) - tmp *= -1; + tmp = -tmp; if (tmp < best_diff) { if (best_parent) { free(best_parent->path); @@ -176,41 +189,44 @@ out: return ret; } -static int add_clone_source(struct btrfs_send *s, u64 root_id) +static int add_clone_source(struct btrfs_send *sctx, u64 root_id) { void *tmp; - tmp = s->clone_sources; - s->clone_sources = realloc(s->clone_sources, - sizeof(*s->clone_sources) * (s->clone_sources_count + 1)); + tmp = sctx->clone_sources; + sctx->clone_sources = realloc(sctx->clone_sources, + sizeof(*sctx->clone_sources) * (sctx->clone_sources_count + 1)); - if (!s->clone_sources) { + if (!sctx->clone_sources) { free(tmp); return -ENOMEM; } - s->clone_sources[s->clone_sources_count++] = root_id; + sctx->clone_sources[sctx->clone_sources_count++] = root_id; return 0; } -static int write_buf(int fd, const void *buf, int size) +#if 0 +static int write_buf(int fd, const char *buf, size_t size) { int ret; - int pos = 0; + size_t pos = 0; while (pos < size) { - ret = write(fd, (char*)buf + pos, size - pos); - if (ret < 0) { + ssize_t wbytes; + + wbytes = write(fd, buf + pos, size - pos); + if (wbytes < 0) { ret = -errno; error("failed to dump stream: %s", strerror(-ret)); goto out; } - if (!ret) { + if (!wbytes) { ret = -EIO; error("failed to dump stream: %s", strerror(-ret)); goto out; } - pos += ret; + pos += wbytes; } ret = 0; @@ -218,40 +234,71 @@ out: return ret; } -static void *dump_thread(void *arg_) +static void* read_sent_data_copy(void *arg) { int ret; - struct btrfs_send *s = (struct btrfs_send*)arg_; - char buf[4096]; - int readed; + struct btrfs_send *sctx = (struct btrfs_send*)arg; + char buf[SEND_BUFFER_SIZE]; while (1) { - readed = read(s->send_fd, buf, sizeof(buf)); - if (readed < 0) { + ssize_t rbytes; + + rbytes = read(sctx->send_fd, buf, sizeof(buf)); + if (rbytes < 0) { ret = -errno; - error("failed to read stream from kernel: %s\n", + error("failed to read stream from kernel: %s", strerror(-ret)); goto out; } - if (!readed) { + if (!rbytes) { ret = 0; goto out; } - ret = write_buf(s->dump_fd, buf, readed); + ret = write_buf(sctx->dump_fd, buf, rbytes); if (ret < 0) goto out; } out: - if (ret < 0) { + if (ret < 0) exit(-ret); + + return ERR_PTR(ret); +} +#endif + +static void *read_sent_data(void *arg) +{ + int ret; + struct btrfs_send *sctx = (struct btrfs_send*)arg; + + while (1) { + ssize_t sbytes; + + /* Source is a pipe, output is either file or stdout */ + sbytes = splice(sctx->send_fd, NULL, sctx->dump_fd, + NULL, SEND_BUFFER_SIZE, SPLICE_F_MORE); + if (sbytes < 0) { + ret = -errno; + error("failed to read stream from kernel: %s", + strerror(-ret)); + goto out; + } + if (!sbytes) { + ret = 0; + goto out; + } } +out: + if (ret < 0) + exit(-ret); + return ERR_PTR(ret); } static int do_send(struct btrfs_send *send, u64 parent_root_id, - int is_first_subvol, int is_last_subvol, char *subvol, + int is_first_subvol, int is_last_subvol, const char *subvol, u64 flags) { int ret; @@ -280,8 +327,7 @@ static int do_send(struct btrfs_send *send, u64 parent_root_id, send->send_fd = pipefd[0]; if (!ret) - ret = pthread_create(&t_read, NULL, dump_thread, - send); + ret = pthread_create(&t_read, NULL, read_sent_data, send); if (ret) { ret = -ret; error("thread setup failed: %s", strerror(-ret)); @@ -339,14 +385,14 @@ out: return ret; } -static int init_root_path(struct btrfs_send *s, const char *subvol) +static int init_root_path(struct btrfs_send *sctx, const char *subvol) { int ret = 0; - if (s->root_path) + if (sctx->root_path) goto out; - ret = find_mount_root(subvol, &s->root_path); + ret = find_mount_root(subvol, &sctx->root_path); if (ret < 0) { error("failed to determine mount point for %s: %s", subvol, strerror(-ret)); @@ -359,14 +405,14 @@ static int init_root_path(struct btrfs_send *s, const char *subvol) goto out; } - s->mnt_fd = open(s->root_path, O_RDONLY | O_NOATIME); - if (s->mnt_fd < 0) { + sctx->mnt_fd = open(sctx->root_path, O_RDONLY | O_NOATIME); + if (sctx->mnt_fd < 0) { ret = -errno; - error("cannot open '%s': %s", s->root_path, strerror(-ret)); + error("cannot open '%s': %s", sctx->root_path, strerror(-ret)); goto out; } - ret = subvol_uuid_search_init(s->mnt_fd, &s->sus); + ret = subvol_uuid_search_init(sctx->mnt_fd, &sctx->sus); if (ret < 0) { error("failed to initialize subvol search: %s", strerror(-ret)); @@ -378,13 +424,13 @@ out: } -static int is_subvol_ro(struct btrfs_send *s, char *subvol) +static int is_subvol_ro(struct btrfs_send *sctx, const char *subvol) { int ret; u64 flags; int fd = -1; - fd = openat(s->mnt_fd, subvol, O_RDONLY | O_NOATIME); + fd = openat(sctx->mnt_fd, subvol, O_RDONLY | O_NOATIME); if (fd < 0) { ret = -errno; error("cannot open %s: %s", subvol, strerror(-ret)); @@ -411,6 +457,37 @@ out: return ret; } +static int set_root_info(struct btrfs_send *sctx, const char *subvol, + u64 *root_id) +{ + int ret; + + ret = init_root_path(sctx, subvol); + if (ret < 0) + goto out; + + ret = get_root_id(sctx, subvol_strip_mountpoint(sctx->root_path, subvol), + root_id); + if (ret < 0) { + error("cannot resolve rootid for %s", subvol); + goto out; + } + +out: + return ret; +} + +static void free_send_info(struct btrfs_send *sctx) +{ + if (sctx->mnt_fd >= 0) { + close(sctx->mnt_fd); + sctx->mnt_fd = -1; + } + free(sctx->root_path); + sctx->root_path = NULL; + subvol_uuid_search_finit(&sctx->sus); +} + int cmd_send(int argc, char **argv) { char *subvol = NULL; @@ -460,18 +537,10 @@ int cmd_send(int argc, char **argv) goto out; } - ret = init_root_path(&send, subvol); + ret = set_root_info(&send, subvol, &root_id); if (ret < 0) goto out; - ret = get_root_id(&send, - subvol_strip_mountpoint(send.root_path, subvol), - &root_id); - if (ret < 0) { - error("cannot resolve rootid for %s", subvol); - goto out; - } - ret = is_subvol_ro(&send, subvol); if (ret < 0) goto out; @@ -486,15 +555,9 @@ int cmd_send(int argc, char **argv) error("cannot add clone source: %s", strerror(-ret)); goto out; } - subvol_uuid_search_finit(&send.sus); free(subvol); subvol = NULL; - if (send.mnt_fd >= 0) { - close(send.mnt_fd); - send.mnt_fd = -1; - } - free(send.root_path); - send.root_path = NULL; + free_send_info(&send); full_send = 0; break; case 'f': @@ -548,7 +611,20 @@ int cmd_send(int argc, char **argv) usage(cmd_send_usage); if (outname[0]) { - send.dump_fd = creat(outname, 0600); + int tmpfd; + + /* + * Try to use an existing file first. Even if send runs as + * root, it might not have permissions to create file (eg. on a + * NFS) but it should still be able to use a pre-created file. + */ + tmpfd = open(outname, O_WRONLY | O_TRUNC); + if (tmpfd < 0) { + if (errno == ENOENT) + tmpfd = open(outname, + O_CREAT | O_WRONLY | O_TRUNC, 0600); + } + send.dump_fd = tmpfd; if (send.dump_fd == -1) { ret = -errno; error("cannot create '%s': %s", outname, strerror(-ret)); @@ -564,8 +640,6 @@ int cmd_send(int argc, char **argv) } /* use first send subvol to determine mount_root */ - subvol = argv[optind]; - subvol = realpath(argv[optind], NULL); if (!subvol) { ret = -errno; @@ -652,7 +726,11 @@ int cmd_send(int argc, char **argv) goto out; } - if (!full_send && !parent_root_id) { + if (!full_send && !snapshot_parent) { + ret = set_root_info(&send, subvol, &root_id); + if (ret < 0) + goto out; + ret = find_good_parent(&send, root_id, &parent_root_id); if (ret < 0) { error("parent determination failed for %lld", @@ -661,15 +739,6 @@ int cmd_send(int argc, char **argv) } } - ret = is_subvol_ro(&send, subvol); - if (ret < 0) - goto out; - if (!ret) { - ret = -EINVAL; - error("subvolume %s is not read-only", subvol); - goto out; - } - if (new_end_cmd_semantic) { /* require new kernel */ is_first_subvol = (i == optind); @@ -684,16 +753,15 @@ int cmd_send(int argc, char **argv) if (ret < 0) goto out; - if (!full_send) { + if (!full_send && !snapshot_parent) { /* done with this subvol, so add it to the clone sources */ ret = add_clone_source(&send, root_id); if (ret < 0) { error("cannot add clone source: %s", strerror(-ret)); goto out; } + free_send_info(&send); } - - parent_root_id = 0; } ret = 0; @@ -702,10 +770,7 @@ out: free(subvol); free(snapshot_parent); free(send.clone_sources); - if (send.mnt_fd >= 0) - close(send.mnt_fd); - free(send.root_path); - subvol_uuid_search_finit(&send.sus); + free_send_info(&send); return !!ret; } diff --git a/cmds-subvolume.c b/cmds-subvolume.c index b3f7cbcb..7384de45 100644 --- a/cmds-subvolume.c +++ b/cmds-subvolume.c @@ -431,10 +431,10 @@ static int cmd_subvol_list(int argc, char **argv) u64 top_id; int ret = -1, uerr = 0; char *subvol; - int is_tab_result = 0; int is_list_all = 0; int is_only_in_path = 0; DIR *dirstream = NULL; + enum btrfs_list_layout layout = BTRFS_LIST_LAYOUT_DEFAULT; filter_set = btrfs_list_alloc_filter_set(); comparer_set = btrfs_list_alloc_comparer_set(); @@ -473,7 +473,7 @@ static int cmd_subvol_list(int argc, char **argv) is_only_in_path = 1; break; case 't': - is_tab_result = 1; + layout = BTRFS_LIST_LAYOUT_TABLE; break; case 's': btrfs_list_setup_filter(&filter_set, @@ -530,10 +530,6 @@ static int cmd_subvol_list(int argc, char **argv) } } - if (flags) - btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FLAGS, - flags); - if (check_argc_exact(argc - optind, 1)) { uerr = 1; goto out; @@ -547,11 +543,13 @@ static int cmd_subvol_list(int argc, char **argv) goto out; } + if (flags) + btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FLAGS, + flags); + ret = btrfs_list_get_path_rootid(fd, &top_id); - if (ret) { - error("can't get rootid for '%s'", subvol); + if (ret) goto out; - } if (is_list_all) btrfs_list_setup_filter(&filter_set, @@ -568,21 +566,15 @@ static int cmd_subvol_list(int argc, char **argv) btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL); btrfs_list_setup_print_column(BTRFS_LIST_PATH); - if (is_tab_result) - ret = btrfs_list_subvols_print(fd, filter_set, comparer_set, - BTRFS_LIST_LAYOUT_TABLE, - !is_list_all && !is_only_in_path, NULL); - else - ret = btrfs_list_subvols_print(fd, filter_set, comparer_set, - BTRFS_LIST_LAYOUT_DEFAULT, - !is_list_all && !is_only_in_path, NULL); + ret = btrfs_list_subvols_print(fd, filter_set, comparer_set, + layout, !is_list_all && !is_only_in_path, NULL); out: close_file_or_dir(fd, dirstream); if (filter_set) - btrfs_list_free_filter_set(filter_set); + free(filter_set); if (comparer_set) - btrfs_list_free_comparer_set(comparer_set); + free(comparer_set); if (uerr) usage(cmd_subvol_list_usage); return !!ret; @@ -803,7 +795,7 @@ static int cmd_subvol_get_default(int argc, char **argv) BTRFS_LIST_LAYOUT_DEFAULT, 1, NULL); if (filter_set) - btrfs_list_free_filter_set(filter_set); + free(filter_set); out: close_file_or_dir(fd, dirstream); return !!ret; @@ -929,21 +921,12 @@ static int cmd_subvol_show(int argc, char **argv) } ret = get_subvol_info(fullpath, &get_ri); - if (ret == 2) { - /* - * Since the top level btrfs was given don't - * take that as error - */ - printf("%s is toplevel subvolume\n", fullpath); - ret = 0; - goto out; - } if (ret) { if (ret < 0) { - error("Failed to get subvol info %s: %s\n", + error("Failed to get subvol info %s: %s", fullpath, strerror(-ret)); } else { - error("Failed to get subvol info %s: %d\n", + error("Failed to get subvol info %s: %d", fullpath, ret); } return ret; @@ -1011,7 +994,7 @@ out: free(get_ri.path); free(get_ri.name); free(get_ri.full_path); - btrfs_list_free_filter_set(filter_set); + free(filter_set); close_file_or_dir(fd, dirstream1); free(fullpath); @@ -1251,7 +1234,7 @@ static int cmd_subvol_sync(int argc, char **argv) } if (id < BTRFS_FIRST_FREE_OBJECTID || id > BTRFS_LAST_FREE_OBJECTID) { - error("subvolume id %s out of range\n", arg); + error("subvolume id %s out of range", arg); ret = 1; goto out; } diff --git a/config.h.in b/config.h.in index 20838444..42167c0a 100644 --- a/config.h.in +++ b/config.h.in @@ -27,6 +27,9 @@ /* Define to 1 if you have the `openat' function. */ #undef HAVE_OPENAT +/* We did not define FIEMAP_EXTENT_SHARED */ +#undef HAVE_OWN_FIEMAP_EXTENT_SHARED_DEFINE + /* Define to 1 if you have the <stdint.h> header file. */ #undef HAVE_STDINT_H diff --git a/config/config.guess b/config/config.guess index c6fad2f5..eab94e23 100755 --- a/config/config.guess +++ b/config/config.guess @@ -1,8 +1,8 @@ #! /bin/sh # Attempt to guess a canonical system name. -# Copyright 1992-2013 Free Software Foundation, Inc. +# Copyright 1992-2014 Free Software Foundation, Inc. -timestamp='2013-06-10' +timestamp='2014-11-04' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by @@ -24,12 +24,12 @@ timestamp='2013-06-10' # program. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # -# Originally written by Per Bothner. +# Originally written by Per Bothner; maintained since 2000 by Ben Elliston. # # You can get the latest version of this script from: # http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD # -# Please send patches with a ChangeLog entry to config-patches@gnu.org. +# Please send patches to <config-patches@gnu.org>. me=`echo "$0" | sed -e 's,.*/,,'` @@ -50,7 +50,7 @@ version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. -Copyright 1992-2013 Free Software Foundation, Inc. +Copyright 1992-2014 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." @@ -149,7 +149,7 @@ Linux|GNU|GNU/*) LIBC=gnu #endif EOF - eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'` + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC' | sed 's, ,,g'` ;; esac @@ -589,8 +589,9 @@ EOF else IBM_ARCH=powerpc fi - if [ -x /usr/bin/oslevel ] ; then - IBM_REV=`/usr/bin/oslevel` + if [ -x /usr/bin/lslpp ] ; then + IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | + awk -F: '{ print $3 }' | sed s/[0-9]*$/0/` else IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} fi @@ -836,7 +837,7 @@ EOF *:MINGW*:*) echo ${UNAME_MACHINE}-pc-mingw32 exit ;; - i*:MSYS*:*) + *:MSYS*:*) echo ${UNAME_MACHINE}-pc-msys exit ;; i*:windows32*:*) @@ -979,10 +980,10 @@ EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` test x"${CPU}" != x && { echo "${CPU}-${VENDOR}-linux-${LIBC}"; exit; } ;; - or1k:Linux:*:*) - echo ${UNAME_MACHINE}-${VENDOR}-linux-${LIBC} + openrisc*:Linux:*:*) + echo or1k-${VENDOR}-linux-${LIBC} exit ;; - or32:Linux:*:*) + or32:Linux:*:* | or1k*:Linux:*:*) echo ${UNAME_MACHINE}-${VENDOR}-linux-${LIBC} exit ;; padre:Linux:*:*) @@ -1270,16 +1271,26 @@ EOF if test "$UNAME_PROCESSOR" = unknown ; then UNAME_PROCESSOR=powerpc fi - if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then - if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ - (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ - grep IS_64BIT_ARCH >/dev/null - then - case $UNAME_PROCESSOR in - i386) UNAME_PROCESSOR=x86_64 ;; - powerpc) UNAME_PROCESSOR=powerpc64 ;; - esac + if test `echo "$UNAME_RELEASE" | sed -e 's/\..*//'` -le 10 ; then + if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then + if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + case $UNAME_PROCESSOR in + i386) UNAME_PROCESSOR=x86_64 ;; + powerpc) UNAME_PROCESSOR=powerpc64 ;; + esac + fi fi + elif test "$UNAME_PROCESSOR" = i386 ; then + # Avoid executing cc on OS X 10.9, as it ships with a stub + # that puts up a graphical alert prompting to install + # developer tools. Any system running Mac OS X 10.7 or + # later (Darwin 11 and later) is required to have a 64-bit + # processor. This is not true of the ARM version of Darwin + # that Apple uses in portable devices. + UNAME_PROCESSOR=x86_64 fi echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} exit ;; @@ -1371,154 +1382,6 @@ EOF exit ;; esac -eval $set_cc_for_build -cat >$dummy.c <<EOF -#ifdef _SEQUENT_ -# include <sys/types.h> -# include <sys/utsname.h> -#endif -main () -{ -#if defined (sony) -#if defined (MIPSEB) - /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, - I don't know.... */ - printf ("mips-sony-bsd\n"); exit (0); -#else -#include <sys/param.h> - printf ("m68k-sony-newsos%s\n", -#ifdef NEWSOS4 - "4" -#else - "" -#endif - ); exit (0); -#endif -#endif - -#if defined (__arm) && defined (__acorn) && defined (__unix) - printf ("arm-acorn-riscix\n"); exit (0); -#endif - -#if defined (hp300) && !defined (hpux) - printf ("m68k-hp-bsd\n"); exit (0); -#endif - -#if defined (NeXT) -#if !defined (__ARCHITECTURE__) -#define __ARCHITECTURE__ "m68k" -#endif - int version; - version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; - if (version < 4) - printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); - else - printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); - exit (0); -#endif - -#if defined (MULTIMAX) || defined (n16) -#if defined (UMAXV) - printf ("ns32k-encore-sysv\n"); exit (0); -#else -#if defined (CMU) - printf ("ns32k-encore-mach\n"); exit (0); -#else - printf ("ns32k-encore-bsd\n"); exit (0); -#endif -#endif -#endif - -#if defined (__386BSD__) - printf ("i386-pc-bsd\n"); exit (0); -#endif - -#if defined (sequent) -#if defined (i386) - printf ("i386-sequent-dynix\n"); exit (0); -#endif -#if defined (ns32000) - printf ("ns32k-sequent-dynix\n"); exit (0); -#endif -#endif - -#if defined (_SEQUENT_) - struct utsname un; - - uname(&un); - - if (strncmp(un.version, "V2", 2) == 0) { - printf ("i386-sequent-ptx2\n"); exit (0); - } - if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ - printf ("i386-sequent-ptx1\n"); exit (0); - } - printf ("i386-sequent-ptx\n"); exit (0); - -#endif - -#if defined (vax) -# if !defined (ultrix) -# include <sys/param.h> -# if defined (BSD) -# if BSD == 43 - printf ("vax-dec-bsd4.3\n"); exit (0); -# else -# if BSD == 199006 - printf ("vax-dec-bsd4.3reno\n"); exit (0); -# else - printf ("vax-dec-bsd\n"); exit (0); -# endif -# endif -# else - printf ("vax-dec-bsd\n"); exit (0); -# endif -# else - printf ("vax-dec-ultrix\n"); exit (0); -# endif -#endif - -#if defined (alliant) && defined (i860) - printf ("i860-alliant-bsd\n"); exit (0); -#endif - - exit (1); -} -EOF - -$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` && - { echo "$SYSTEM_NAME"; exit; } - -# Apollos put the system type in the environment. - -test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; } - -# Convex versions that predate uname can use getsysinfo(1) - -if [ -x /usr/convex/getsysinfo ] -then - case `getsysinfo -f cpu_type` in - c1*) - echo c1-convex-bsd - exit ;; - c2*) - if getsysinfo -f scalar_acc - then echo c32-convex-bsd - else echo c2-convex-bsd - fi - exit ;; - c34*) - echo c34-convex-bsd - exit ;; - c38*) - echo c38-convex-bsd - exit ;; - c4*) - echo c4-convex-bsd - exit ;; - esac -fi - cat >&2 <<EOF $0: unable to guess system type diff --git a/config/config.sub b/config/config.sub index 8b612ab8..7ffe3737 100755 --- a/config/config.sub +++ b/config/config.sub @@ -1,8 +1,8 @@ #! /bin/sh # Configuration validation subroutine script. -# Copyright 1992-2013 Free Software Foundation, Inc. +# Copyright 1992-2014 Free Software Foundation, Inc. -timestamp='2013-04-24' +timestamp='2014-12-03' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by @@ -25,7 +25,7 @@ timestamp='2013-04-24' # of the GNU General Public License, version 3 ("GPLv3"). -# Please send patches with a ChangeLog entry to config-patches@gnu.org. +# Please send patches to <config-patches@gnu.org>. # # Configuration subroutine to validate and canonicalize a configuration type. # Supply the specified configuration type as an argument. @@ -68,7 +68,7 @@ Report bugs and patches to <config-patches@gnu.org>." version="\ GNU config.sub ($timestamp) -Copyright 1992-2013 Free Software Foundation, Inc. +Copyright 1992-2014 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." @@ -257,7 +257,7 @@ case $basic_machine in | avr | avr32 \ | be32 | be64 \ | bfin \ - | c4x | clipper \ + | c4x | c8051 | clipper \ | d10v | d30v | dlx | dsp16xx \ | epiphany \ | fido | fr30 | frv \ @@ -265,6 +265,7 @@ case $basic_machine in | hexagon \ | i370 | i860 | i960 | ia64 \ | ip2k | iq2000 \ + | k1om \ | le32 | le64 \ | lm32 \ | m32c | m32r | m32rle | m68000 | m68k | m88k \ @@ -282,8 +283,10 @@ case $basic_machine in | mips64vr5900 | mips64vr5900el \ | mipsisa32 | mipsisa32el \ | mipsisa32r2 | mipsisa32r2el \ + | mipsisa32r6 | mipsisa32r6el \ | mipsisa64 | mipsisa64el \ | mipsisa64r2 | mipsisa64r2el \ + | mipsisa64r6 | mipsisa64r6el \ | mipsisa64sb1 | mipsisa64sb1el \ | mipsisa64sr71k | mipsisa64sr71kel \ | mipsr5900 | mipsr5900el \ @@ -295,11 +298,11 @@ case $basic_machine in | nds32 | nds32le | nds32be \ | nios | nios2 | nios2eb | nios2el \ | ns16k | ns32k \ - | open8 \ - | or1k | or32 \ + | open8 | or1k | or1knd | or32 \ | pdp10 | pdp11 | pj | pjl \ | powerpc | powerpc64 | powerpc64le | powerpcle \ | pyramid \ + | riscv32 | riscv64 \ | rl78 | rx \ | score \ | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ @@ -310,6 +313,7 @@ case $basic_machine in | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \ | ubicom32 \ | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ + | visium \ | we32k \ | x86 | xc16x | xstormy16 | xtensa \ | z8k | z80) @@ -324,7 +328,10 @@ case $basic_machine in c6x) basic_machine=tic6x-unknown ;; - m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | picochip) + leon|leon[3-9]) + basic_machine=sparc-$basic_machine + ;; + m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip) basic_machine=$basic_machine-unknown os=-none ;; @@ -372,7 +379,7 @@ case $basic_machine in | be32-* | be64-* \ | bfin-* | bs2000-* \ | c[123]* | c30-* | [cjt]90-* | c4x-* \ - | clipper-* | craynv-* | cydra-* \ + | c8051-* | clipper-* | craynv-* | cydra-* \ | d10v-* | d30v-* | dlx-* \ | elxsi-* \ | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ @@ -381,6 +388,7 @@ case $basic_machine in | hexagon-* \ | i*86-* | i860-* | i960-* | ia64-* \ | ip2k-* | iq2000-* \ + | k1om-* \ | le32-* | le64-* \ | lm32-* \ | m32c-* | m32r-* | m32rle-* \ @@ -400,8 +408,10 @@ case $basic_machine in | mips64vr5900-* | mips64vr5900el-* \ | mipsisa32-* | mipsisa32el-* \ | mipsisa32r2-* | mipsisa32r2el-* \ + | mipsisa32r6-* | mipsisa32r6el-* \ | mipsisa64-* | mipsisa64el-* \ | mipsisa64r2-* | mipsisa64r2el-* \ + | mipsisa64r6-* | mipsisa64r6el-* \ | mipsisa64sb1-* | mipsisa64sb1el-* \ | mipsisa64sr71k-* | mipsisa64sr71kel-* \ | mipsr5900-* | mipsr5900el-* \ @@ -413,6 +423,7 @@ case $basic_machine in | nios-* | nios2-* | nios2eb-* | nios2el-* \ | none-* | np1-* | ns16k-* | ns32k-* \ | open8-* \ + | or1k*-* \ | orion-* \ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ @@ -430,6 +441,7 @@ case $basic_machine in | ubicom32-* \ | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \ | vax-* \ + | visium-* \ | we32k-* \ | x86-* | x86_64-* | xc16x-* | xps100-* \ | xstormy16-* | xtensa*-* \ @@ -767,6 +779,9 @@ case $basic_machine in basic_machine=m68k-isi os=-sysv ;; + leon-*|leon[3-9]-*) + basic_machine=sparc-`echo $basic_machine | sed 's/-.*//'` + ;; m68knommu) basic_machine=m68k-unknown os=-linux @@ -794,7 +809,7 @@ case $basic_machine in os=-mingw64 ;; mingw32) - basic_machine=i386-pc + basic_machine=i686-pc os=-mingw32 ;; mingw32ce) @@ -822,6 +837,10 @@ case $basic_machine in basic_machine=powerpc-unknown os=-morphos ;; + moxiebox) + basic_machine=moxie-unknown + os=-moxiebox + ;; msdos) basic_machine=i386-pc os=-msdos @@ -830,7 +849,7 @@ case $basic_machine in basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` ;; msys) - basic_machine=i386-pc + basic_machine=i686-pc os=-msys ;; mvs) @@ -1367,14 +1386,14 @@ case $os in | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \ | -linux-newlib* | -linux-musl* | -linux-uclibc* \ - | -uxpv* | -beos* | -mpeix* | -udk* \ + | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \ | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ - | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es*) + | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* | -tirtos*) # Remember, each alternative MUST END IN *, to match a version number. ;; -qnx*) @@ -1546,6 +1565,9 @@ case $basic_machine in c4x-* | tic4x-*) os=-coff ;; + c8051-*) + os=-elf + ;; hexagon-*) os=-elf ;; @@ -1589,9 +1611,6 @@ case $basic_machine in mips*-*) os=-elf ;; - or1k-*) - os=-elf - ;; or32-*) os=-coff ;; diff --git a/config/install-sh b/config/install-sh index 377bb868..0b0fdcbb 100755 --- a/config/install-sh +++ b/config/install-sh @@ -1,7 +1,7 @@ #!/bin/sh # install - install a program, script, or datafile -scriptversion=2011-11-20.07; # UTC +scriptversion=2013-12-25.23; # UTC # This originates from X11R5 (mit/util/scripts/install.sh), which was # later released in X11R6 (xc/config/util/install.sh) with the @@ -41,19 +41,15 @@ scriptversion=2011-11-20.07; # UTC # This script is compatible with the BSD install script, but was written # from scratch. +tab=' ' nl=' ' -IFS=" "" $nl" +IFS=" $tab$nl" -# set DOITPROG to echo to test this script +# Set DOITPROG to "echo" to test this script. -# Don't use :- since 4.3BSD and earlier shells don't like it. doit=${DOITPROG-} -if test -z "$doit"; then - doit_exec=exec -else - doit_exec=$doit -fi +doit_exec=${doit:-exec} # Put in absolute file names if you don't have them in your path; # or use environment vars. @@ -68,17 +64,6 @@ mvprog=${MVPROG-mv} rmprog=${RMPROG-rm} stripprog=${STRIPPROG-strip} -posix_glob='?' -initialize_posix_glob=' - test "$posix_glob" != "?" || { - if (set -f) 2>/dev/null; then - posix_glob= - else - posix_glob=: - fi - } -' - posix_mkdir= # Desired mode of installed file. @@ -97,7 +82,7 @@ dir_arg= dst_arg= copy_on_change=false -no_target_directory= +is_target_a_directory=possibly usage="\ Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE @@ -137,46 +122,57 @@ while test $# -ne 0; do -d) dir_arg=true;; -g) chgrpcmd="$chgrpprog $2" - shift;; + shift;; --help) echo "$usage"; exit $?;; -m) mode=$2 - case $mode in - *' '* | *' '* | *' -'* | *'*'* | *'?'* | *'['*) - echo "$0: invalid mode: $mode" >&2 - exit 1;; - esac - shift;; + case $mode in + *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*) + echo "$0: invalid mode: $mode" >&2 + exit 1;; + esac + shift;; -o) chowncmd="$chownprog $2" - shift;; + shift;; -s) stripcmd=$stripprog;; - -t) dst_arg=$2 - # Protect names problematic for 'test' and other utilities. - case $dst_arg in - -* | [=\(\)!]) dst_arg=./$dst_arg;; - esac - shift;; + -t) + is_target_a_directory=always + dst_arg=$2 + # Protect names problematic for 'test' and other utilities. + case $dst_arg in + -* | [=\(\)!]) dst_arg=./$dst_arg;; + esac + shift;; - -T) no_target_directory=true;; + -T) is_target_a_directory=never;; --version) echo "$0 $scriptversion"; exit $?;; - --) shift - break;; + --) shift + break;; - -*) echo "$0: invalid option: $1" >&2 - exit 1;; + -*) echo "$0: invalid option: $1" >&2 + exit 1;; *) break;; esac shift done +# We allow the use of options -d and -T together, by making -d +# take the precedence; this is for compatibility with GNU install. + +if test -n "$dir_arg"; then + if test -n "$dst_arg"; then + echo "$0: target directory not allowed when installing a directory." >&2 + exit 1 + fi +fi + if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then # When -d is used, all remaining arguments are directories to create. # When -t is used, the destination is already specified. @@ -208,6 +204,15 @@ if test $# -eq 0; then fi if test -z "$dir_arg"; then + if test $# -gt 1 || test "$is_target_a_directory" = always; then + if test ! -d "$dst_arg"; then + echo "$0: $dst_arg: Is not a directory." >&2 + exit 1 + fi + fi +fi + +if test -z "$dir_arg"; then do_exit='(exit $ret); exit $ret' trap "ret=129; $do_exit" 1 trap "ret=130; $do_exit" 2 @@ -223,16 +228,16 @@ if test -z "$dir_arg"; then *[0-7]) if test -z "$stripcmd"; then - u_plus_rw= + u_plus_rw= else - u_plus_rw='% 200' + u_plus_rw='% 200' fi cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; *) if test -z "$stripcmd"; then - u_plus_rw= + u_plus_rw= else - u_plus_rw=,u+rw + u_plus_rw=,u+rw fi cp_umask=$mode$u_plus_rw;; esac @@ -269,41 +274,15 @@ do # If destination is a directory, append the input filename; won't work # if double slashes aren't ignored. if test -d "$dst"; then - if test -n "$no_target_directory"; then - echo "$0: $dst_arg: Is a directory" >&2 - exit 1 + if test "$is_target_a_directory" = never; then + echo "$0: $dst_arg: Is a directory" >&2 + exit 1 fi dstdir=$dst dst=$dstdir/`basename "$src"` dstdir_status=0 else - # Prefer dirname, but fall back on a substitute if dirname fails. - dstdir=` - (dirname "$dst") 2>/dev/null || - expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$dst" : 'X\(//\)[^/]' \| \ - X"$dst" : 'X\(//\)$' \| \ - X"$dst" : 'X\(/\)' \| . 2>/dev/null || - echo X"$dst" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q' - ` - + dstdir=`dirname "$dst"` test -d "$dstdir" dstdir_status=$? fi @@ -314,74 +293,74 @@ do if test $dstdir_status != 0; then case $posix_mkdir in '') - # Create intermediate dirs using mode 755 as modified by the umask. - # This is like FreeBSD 'install' as of 1997-10-28. - umask=`umask` - case $stripcmd.$umask in - # Optimize common cases. - *[2367][2367]) mkdir_umask=$umask;; - .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; - - *[0-7]) - mkdir_umask=`expr $umask + 22 \ - - $umask % 100 % 40 + $umask % 20 \ - - $umask % 10 % 4 + $umask % 2 - `;; - *) mkdir_umask=$umask,go-w;; - esac - - # With -d, create the new directory with the user-specified mode. - # Otherwise, rely on $mkdir_umask. - if test -n "$dir_arg"; then - mkdir_mode=-m$mode - else - mkdir_mode= - fi - - posix_mkdir=false - case $umask in - *[123567][0-7][0-7]) - # POSIX mkdir -p sets u+wx bits regardless of umask, which - # is incompatible with FreeBSD 'install' when (umask & 300) != 0. - ;; - *) - tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ - trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 - - if (umask $mkdir_umask && - exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 - then - if test -z "$dir_arg" || { - # Check for POSIX incompatibilities with -m. - # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or - # other-writable bit of parent directory when it shouldn't. - # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. - ls_ld_tmpdir=`ls -ld "$tmpdir"` - case $ls_ld_tmpdir in - d????-?r-*) different_mode=700;; - d????-?--*) different_mode=755;; - *) false;; - esac && - $mkdirprog -m$different_mode -p -- "$tmpdir" && { - ls_ld_tmpdir_1=`ls -ld "$tmpdir"` - test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" - } - } - then posix_mkdir=: - fi - rmdir "$tmpdir/d" "$tmpdir" - else - # Remove any dirs left behind by ancient mkdir implementations. - rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null - fi - trap '' 0;; - esac;; + # Create intermediate dirs using mode 755 as modified by the umask. + # This is like FreeBSD 'install' as of 1997-10-28. + umask=`umask` + case $stripcmd.$umask in + # Optimize common cases. + *[2367][2367]) mkdir_umask=$umask;; + .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; + + *[0-7]) + mkdir_umask=`expr $umask + 22 \ + - $umask % 100 % 40 + $umask % 20 \ + - $umask % 10 % 4 + $umask % 2 + `;; + *) mkdir_umask=$umask,go-w;; + esac + + # With -d, create the new directory with the user-specified mode. + # Otherwise, rely on $mkdir_umask. + if test -n "$dir_arg"; then + mkdir_mode=-m$mode + else + mkdir_mode= + fi + + posix_mkdir=false + case $umask in + *[123567][0-7][0-7]) + # POSIX mkdir -p sets u+wx bits regardless of umask, which + # is incompatible with FreeBSD 'install' when (umask & 300) != 0. + ;; + *) + tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ + trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 + + if (umask $mkdir_umask && + exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 + then + if test -z "$dir_arg" || { + # Check for POSIX incompatibilities with -m. + # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or + # other-writable bit of parent directory when it shouldn't. + # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. + ls_ld_tmpdir=`ls -ld "$tmpdir"` + case $ls_ld_tmpdir in + d????-?r-*) different_mode=700;; + d????-?--*) different_mode=755;; + *) false;; + esac && + $mkdirprog -m$different_mode -p -- "$tmpdir" && { + ls_ld_tmpdir_1=`ls -ld "$tmpdir"` + test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" + } + } + then posix_mkdir=: + fi + rmdir "$tmpdir/d" "$tmpdir" + else + # Remove any dirs left behind by ancient mkdir implementations. + rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null + fi + trap '' 0;; + esac;; esac if $posix_mkdir && ( - umask $mkdir_umask && - $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" + umask $mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" ) then : else @@ -391,53 +370,51 @@ do # directory the slow way, step by step, checking for races as we go. case $dstdir in - /*) prefix='/';; - [-=\(\)!]*) prefix='./';; - *) prefix='';; + /*) prefix='/';; + [-=\(\)!]*) prefix='./';; + *) prefix='';; esac - eval "$initialize_posix_glob" - oIFS=$IFS IFS=/ - $posix_glob set -f + set -f set fnord $dstdir shift - $posix_glob set +f + set +f IFS=$oIFS prefixes= for d do - test X"$d" = X && continue - - prefix=$prefix$d - if test -d "$prefix"; then - prefixes= - else - if $posix_mkdir; then - (umask=$mkdir_umask && - $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break - # Don't fail if two instances are running concurrently. - test -d "$prefix" || exit 1 - else - case $prefix in - *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; - *) qprefix=$prefix;; - esac - prefixes="$prefixes '$qprefix'" - fi - fi - prefix=$prefix/ + test X"$d" = X && continue + + prefix=$prefix$d + if test -d "$prefix"; then + prefixes= + else + if $posix_mkdir; then + (umask=$mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break + # Don't fail if two instances are running concurrently. + test -d "$prefix" || exit 1 + else + case $prefix in + *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; + *) qprefix=$prefix;; + esac + prefixes="$prefixes '$qprefix'" + fi + fi + prefix=$prefix/ done if test -n "$prefixes"; then - # Don't fail if two instances are running concurrently. - (umask $mkdir_umask && - eval "\$doit_exec \$mkdirprog $prefixes") || - test -d "$dstdir" || exit 1 - obsolete_mkdir_used=true + # Don't fail if two instances are running concurrently. + (umask $mkdir_umask && + eval "\$doit_exec \$mkdirprog $prefixes") || + test -d "$dstdir" || exit 1 + obsolete_mkdir_used=true fi fi fi @@ -472,15 +449,12 @@ do # If -C, don't bother to copy if it wouldn't change the file. if $copy_on_change && - old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && - new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && - - eval "$initialize_posix_glob" && - $posix_glob set -f && + old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && + new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && + set -f && set X $old && old=:$2:$4:$5:$6 && set X $new && new=:$2:$4:$5:$6 && - $posix_glob set +f && - + set +f && test "$old" = "$new" && $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 then @@ -493,24 +467,24 @@ do # to itself, or perhaps because mv is so ancient that it does not # support -f. { - # Now remove or move aside any old file at destination location. - # We try this two ways since rm can't unlink itself on some - # systems and the destination file might be busy for other - # reasons. In this case, the final cleanup might fail but the new - # file should still install successfully. - { - test ! -f "$dst" || - $doit $rmcmd -f "$dst" 2>/dev/null || - { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && - { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } - } || - { echo "$0: cannot unlink or rename $dst" >&2 - (exit 1); exit 1 - } - } && - - # Now rename the file to the real destination. - $doit $mvcmd "$dsttmp" "$dst" + # Now remove or move aside any old file at destination location. + # We try this two ways since rm can't unlink itself on some + # systems and the destination file might be busy for other + # reasons. In this case, the final cleanup might fail but the new + # file should still install successfully. + { + test ! -f "$dst" || + $doit $rmcmd -f "$dst" 2>/dev/null || + { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && + { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } + } || + { echo "$0: cannot unlink or rename $dst" >&2 + (exit 1); exit 1 + } + } && + + # Now rename the file to the real destination. + $doit $mvcmd "$dsttmp" "$dst" } fi || exit 1 @@ -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.7.3. +# Generated by GNU Autoconf 2.69 for btrfs-progs v4.9.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.7.3' -PACKAGE_STRING='btrfs-progs v4.7.3' +PACKAGE_VERSION='v4.9.1' +PACKAGE_STRING='btrfs-progs v4.9.1' PACKAGE_BUGREPORT='linux-btrfs@vger.kernel.org' PACKAGE_URL='http://btrfs.wiki.kernel.org' @@ -1290,7 +1290,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.7.3 to adapt to many kinds of systems. +\`configure' configures btrfs-progs v4.9.1 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1355,7 +1355,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of btrfs-progs v4.7.3:";; + short | recursive ) echo "Configuration of btrfs-progs v4.9.1:";; esac cat <<\_ACEOF @@ -1471,7 +1471,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -btrfs-progs configure v4.7.3 +btrfs-progs configure v4.9.1 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -1840,7 +1840,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.7.3, which was +It was created by btrfs-progs $as_me v4.9.1, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2197,7 +2197,7 @@ fi LIBBTRFS_MAJOR=0 LIBBTRFS_MINOR=1 -LIBBTRFS_PATCHLEVEL=1 +LIBBTRFS_PATCHLEVEL=2 CFLAGS=${CFLAGS:-"-g -O1 -Wall -D_FORTIFY_SOURCE=2"} @@ -5755,6 +5755,7 @@ if test "$DISABLE_BTRFSCONVERT" = 0 && test "x$convertfs" = "x"; then as_fn_error $? "no filesystems for convert, use --disable-convert instead" "$LINENO" 5 fi +HAVE_OWN_FIEMAP_EXTENT_SHARED_DEFINE=0 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for FIEMAP_EXTENT_SHARED defined in linux/fiemap.h" >&5 $as_echo_n "checking for FIEMAP_EXTENT_SHARED defined in linux/fiemap.h... " >&6; } @@ -5790,7 +5791,19 @@ $as_echo "$ac_cv_defined_FIEMAP_EXTENT_SHARED_linux_fiemap_h" >&6; } if test $ac_cv_defined_FIEMAP_EXTENT_SHARED_linux_fiemap_h != "no"; then : else - as_fn_error $? "no definition of FIEMAP_EXTENT_SHARED found" "$LINENO" 5 + HAVE_OWN_FIEMAP_EXTENT_SHARED_DEFINE=1 + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: no definition of FIEMAP_EXTENT_SHARED found, probably old kernel, will use own defintion, 'btrfs fi du' might report wrong numbers" >&5 +$as_echo "$as_me: WARNING: no definition of FIEMAP_EXTENT_SHARED found, probably old kernel, will use own defintion, 'btrfs fi du' might report wrong numbers" >&2;} +fi + +if test "x$HAVE_OWN_FIEMAP_EXTENT_SHARED_DEFINE" == "x1"; then + +$as_echo "#define HAVE_OWN_FIEMAP_EXTENT_SHARED_DEFINE 1" >>confdefs.h + +else + +$as_echo "#define HAVE_OWN_FIEMAP_EXTENT_SHARED_DEFINE 0" >>confdefs.h + fi @@ -6684,7 +6697,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.7.3, which was +This file was extended by btrfs-progs $as_me v4.9.1, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -6747,7 +6760,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.7.3 +btrfs-progs config.status v4.9.1 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff --git a/configure.ac b/configure.ac index 8fd8f425..b08bfe60 100644 --- a/configure.ac +++ b/configure.ac @@ -10,7 +10,7 @@ fi dnl library version LIBBTRFS_MAJOR=0 LIBBTRFS_MINOR=1 -LIBBTRFS_PATCHLEVEL=1 +LIBBTRFS_PATCHLEVEL=2 CFLAGS=${CFLAGS:-"-g -O1 -Wall -D_FORTIFY_SOURCE=2"} AC_SUBST([CFLAGS]) @@ -144,8 +144,16 @@ if test "$DISABLE_BTRFSCONVERT" = 0 && test "x$convertfs" = "x"; then AC_MSG_ERROR([no filesystems for convert, use --disable-convert instead]) fi +HAVE_OWN_FIEMAP_EXTENT_SHARED_DEFINE=0 AX_CHECK_DEFINE([linux/fiemap.h], [FIEMAP_EXTENT_SHARED], [], - [AC_MSG_ERROR([no definition of FIEMAP_EXTENT_SHARED found])]) + [HAVE_OWN_FIEMAP_EXTENT_SHARED_DEFINE=1 + AC_MSG_WARN([no definition of FIEMAP_EXTENT_SHARED found, probably old kernel, will use own defintion, 'btrfs fi du' might report wrong numbers])]) + +if test "x$HAVE_OWN_FIEMAP_EXTENT_SHARED_DEFINE" == "x1"; then +AC_DEFINE([HAVE_OWN_FIEMAP_EXTENT_SHARED_DEFINE], [1], [We defined FIEMAP_EXTENT_SHARED]) +else +AC_DEFINE([HAVE_OWN_FIEMAP_EXTENT_SHARED_DEFINE], [0], [We did not define FIEMAP_EXTENT_SHARED]) +fi dnl Define <NAME>_LIBS= and <NAME>_CFLAGS= by pkg-config dnl diff --git a/btrfs-convert.c b/convert/main.c index 5175f1f7..8d9f29fa 100644 --- a/btrfs-convert.c +++ b/convert/main.c @@ -103,6 +103,7 @@ struct btrfs_convert_operations { struct btrfs_root *root, int datacsum, int packing, int noxattr, struct task_ctx *p); void (*close_fs)(struct btrfs_convert_context *cctx); + int (*check_state)(struct btrfs_convert_context *cctx); }; static void init_convert_context(struct btrfs_convert_context *cctx) @@ -132,6 +133,11 @@ static inline void convert_close_fs(struct btrfs_convert_context *cctx) cctx->convert_ops->close_fs(cctx); } +static inline int convert_check_state(struct btrfs_convert_context *cctx) +{ + return cctx->convert_ops->check_state(cctx); +} + static int intersect_with_sb(u64 bytenr, u64 num_bytes) { int i; @@ -277,7 +283,7 @@ static int record_file_blocks(struct blk_iterate_data *data, int ret = 0; struct btrfs_root *root = data->root; struct btrfs_root *convert_root = data->convert_root; - struct btrfs_path *path; + struct btrfs_path path; u64 file_pos = file_block * root->sectorsize; u64 old_disk_bytenr = disk_block * root->sectorsize; u64 num_bytes = num_blocks * root->sectorsize; @@ -289,9 +295,7 @@ static int record_file_blocks(struct blk_iterate_data *data, data->objectid, data->inode, file_pos, 0, num_bytes); - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; + btrfs_init_path(&path); /* * Search real disk bytenr from convert root @@ -310,11 +314,11 @@ static int record_file_blocks(struct blk_iterate_data *data, key.type = BTRFS_EXTENT_DATA_KEY; key.offset = cur_off; - ret = btrfs_search_slot(NULL, convert_root, &key, path, 0, 0); + ret = btrfs_search_slot(NULL, convert_root, &key, &path, 0, 0); if (ret < 0) break; if (ret > 0) { - ret = btrfs_previous_item(convert_root, path, + ret = btrfs_previous_item(convert_root, &path, data->convert_ino, BTRFS_EXTENT_DATA_KEY); if (ret < 0) @@ -324,17 +328,17 @@ static int record_file_blocks(struct blk_iterate_data *data, break; } } - node = path->nodes[0]; - slot = path->slots[0]; + node = path.nodes[0]; + slot = path.slots[0]; btrfs_item_key_to_cpu(node, &key, slot); BUG_ON(key.type != BTRFS_EXTENT_DATA_KEY || key.objectid != data->convert_ino || key.offset > cur_off); fi = btrfs_item_ptr(node, slot, struct btrfs_file_extent_item); extent_disk_bytenr = btrfs_file_extent_disk_bytenr(node, fi); - extent_num_bytes = btrfs_file_extent_disk_num_bytes(node, fi); + extent_num_bytes = btrfs_file_extent_num_bytes(node, fi); BUG_ON(cur_off - key.offset >= extent_num_bytes); - btrfs_release_path(path); + btrfs_release_path(&path); if (extent_disk_bytenr) real_disk_bytenr = cur_off - key.offset + @@ -357,7 +361,7 @@ static int record_file_blocks(struct blk_iterate_data *data, * need to waste CPU cycles now. */ } - btrfs_free_path(path); + btrfs_release_path(&path); return ret; } @@ -424,8 +428,16 @@ static int create_image_file_range(struct btrfs_trans_handle *trans, int i; int ret; - BUG_ON(bytenr != round_down(bytenr, root->sectorsize)); - BUG_ON(len != round_down(len, root->sectorsize)); + if (bytenr != round_down(bytenr, root->sectorsize)) { + error("bytenr not sectorsize aligned: %llu", + (unsigned long long)bytenr); + return -EINVAL; + } + if (len != round_down(len, root->sectorsize)) { + error("length not sectorsize aligned: %llu", + (unsigned long long)len); + return -EINVAL; + } len = min_t(u64, len, BTRFS_MAX_EXTENT_SIZE); /* @@ -514,7 +526,11 @@ static int create_image_file_range(struct btrfs_trans_handle *trans, bg_cache->key.offset - bytenr); } - BUG_ON(len != round_down(len, root->sectorsize)); + if (len != round_down(len, root->sectorsize)) { + error("remaining length not sectorsize aligned: %llu", + (unsigned long long)len); + return -EINVAL; + } ret = btrfs_record_file_extent(trans, root, ino, inode, bytenr, disk_bytenr, len); if (ret < 0) @@ -952,7 +968,7 @@ static int create_image(struct btrfs_root *root, { struct btrfs_inode_item buf; struct btrfs_trans_handle *trans; - struct btrfs_path *path = NULL; + struct btrfs_path path; struct btrfs_key key; struct cache_extent *cache; struct cache_tree used_tmp; @@ -969,6 +985,7 @@ static int create_image(struct btrfs_root *root, return -ENOMEM; cache_tree_init(&used_tmp); + btrfs_init_path(&path); ret = btrfs_find_free_objectid(trans, root, BTRFS_FIRST_FREE_OBJECTID, &ino); @@ -985,24 +1002,19 @@ static int create_image(struct btrfs_root *root, if (ret < 0) goto out; - path = btrfs_alloc_path(); - if (!path) { - ret = -ENOMEM; - goto out; - } key.objectid = ino; key.type = BTRFS_INODE_ITEM_KEY; key.offset = 0; - ret = btrfs_search_slot(trans, root, &key, path, 0, 1); + ret = btrfs_search_slot(trans, root, &key, &path, 0, 1); if (ret) { ret = (ret > 0 ? -ENOENT : ret); goto out; } - read_extent_buffer(path->nodes[0], &buf, - btrfs_item_ptr_offset(path->nodes[0], path->slots[0]), + read_extent_buffer(path.nodes[0], &buf, + btrfs_item_ptr_offset(path.nodes[0], path.slots[0]), sizeof(buf)); - btrfs_release_path(path); + btrfs_release_path(&path); /* * Create a new used space cache, which doesn't contain the reserved @@ -1040,18 +1052,18 @@ static int create_image(struct btrfs_root *root, key.objectid = ino; key.type = BTRFS_INODE_ITEM_KEY; key.offset = 0; - ret = btrfs_search_slot(trans, root, &key, path, 0, 1); + ret = btrfs_search_slot(trans, root, &key, &path, 0, 1); if (ret) { ret = (ret > 0 ? -ENOENT : ret); goto out; } btrfs_set_stack_inode_size(&buf, cfg->num_bytes); - write_extent_buffer(path->nodes[0], &buf, - btrfs_item_ptr_offset(path->nodes[0], path->slots[0]), + write_extent_buffer(path.nodes[0], &buf, + btrfs_item_ptr_offset(path.nodes[0], path.slots[0]), sizeof(buf)); out: free_extent_cache_tree(&used_tmp); - btrfs_free_path(path); + btrfs_release_path(&path); btrfs_commit_transaction(trans, root); return ret; } @@ -1063,7 +1075,7 @@ static struct btrfs_root* link_subvol(struct btrfs_root *root, struct btrfs_fs_info *fs_info = root->fs_info; struct btrfs_root *tree_root = fs_info->tree_root; struct btrfs_root *new_root = NULL; - struct btrfs_path *path; + struct btrfs_path path; struct btrfs_inode_item *inode_item; struct extent_buffer *leaf; struct btrfs_key key; @@ -1078,28 +1090,25 @@ static struct btrfs_root* link_subvol(struct btrfs_root *root, if (len == 0 || len > BTRFS_NAME_LEN) return NULL; - path = btrfs_alloc_path(); - if (!path) - return NULL; - + btrfs_init_path(&path); key.objectid = dirid; key.type = BTRFS_DIR_INDEX_KEY; key.offset = (u64)-1; - ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); + ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0); if (ret <= 0) { error("search for DIR_INDEX dirid %llu failed: %d", (unsigned long long)dirid, ret); goto fail; } - if (path->slots[0] > 0) { - path->slots[0]--; - btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); + if (path.slots[0] > 0) { + path.slots[0]--; + btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]); if (key.objectid == dirid && key.type == BTRFS_DIR_INDEX_KEY) index = key.offset + 1; } - btrfs_release_path(path); + btrfs_release_path(&path); trans = btrfs_start_transaction(root, 1); if (!trans) { @@ -1111,14 +1120,14 @@ static struct btrfs_root* link_subvol(struct btrfs_root *root, key.offset = 0; key.type = BTRFS_INODE_ITEM_KEY; - ret = btrfs_lookup_inode(trans, root, path, &key, 1); + ret = btrfs_lookup_inode(trans, root, &path, &key, 1); if (ret) { error("search for INODE_ITEM %llu failed: %d", (unsigned long long)dirid, ret); goto fail; } - leaf = path->nodes[0]; - inode_item = btrfs_item_ptr(leaf, path->slots[0], + leaf = path.nodes[0]; + inode_item = btrfs_item_ptr(leaf, path.slots[0], struct btrfs_inode_item); key.objectid = root_objectid; @@ -1143,7 +1152,7 @@ static struct btrfs_root* link_subvol(struct btrfs_root *root, btrfs_set_inode_size(leaf, inode_item, len * 2 + btrfs_inode_size(leaf, inode_item)); btrfs_mark_buffer_dirty(leaf); - btrfs_release_path(path); + btrfs_release_path(&path); /* add the backref first */ ret = btrfs_add_root_ref(trans, tree_root, root_objectid, @@ -1178,7 +1187,7 @@ static struct btrfs_root* link_subvol(struct btrfs_root *root, new_root = NULL; } fail: - btrfs_free_path(path); + btrfs_init_path(&path); return new_root; } @@ -1351,7 +1360,7 @@ err: /* * Migrate super block to its default position and zero 0 ~ 16k */ -static int migrate_super_block(int fd, u64 old_bytenr, u32 sectorsize) +static int migrate_super_block(int fd, u64 old_bytenr) { int ret; struct extent_buffer *buf; @@ -1359,13 +1368,13 @@ static int migrate_super_block(int fd, u64 old_bytenr, u32 sectorsize) u32 len; u32 bytenr; - buf = malloc(sizeof(*buf) + sectorsize); + buf = malloc(sizeof(*buf) + BTRFS_SUPER_INFO_SIZE); if (!buf) return -ENOMEM; - buf->len = sectorsize; - ret = pread(fd, buf->data, sectorsize, old_bytenr); - if (ret != sectorsize) + buf->len = BTRFS_SUPER_INFO_SIZE; + ret = pread(fd, buf->data, BTRFS_SUPER_INFO_SIZE, old_bytenr); + if (ret != BTRFS_SUPER_INFO_SIZE) goto fail; super = (struct btrfs_super_block *)buf->data; @@ -1373,19 +1382,20 @@ static int migrate_super_block(int fd, u64 old_bytenr, u32 sectorsize) btrfs_set_super_bytenr(super, BTRFS_SUPER_INFO_OFFSET); csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0); - ret = pwrite(fd, buf->data, sectorsize, BTRFS_SUPER_INFO_OFFSET); - if (ret != sectorsize) + ret = pwrite(fd, buf->data, BTRFS_SUPER_INFO_SIZE, + BTRFS_SUPER_INFO_OFFSET); + if (ret != BTRFS_SUPER_INFO_SIZE) goto fail; ret = fsync(fd); if (ret) goto fail; - memset(buf->data, 0, sectorsize); + memset(buf->data, 0, BTRFS_SUPER_INFO_SIZE); for (bytenr = 0; bytenr < BTRFS_SUPER_INFO_OFFSET; ) { len = BTRFS_SUPER_INFO_OFFSET - bytenr; - if (len > sectorsize) - len = sectorsize; + if (len > BTRFS_SUPER_INFO_SIZE) + len = BTRFS_SUPER_INFO_SIZE; ret = pwrite(fd, buf->data, len, bytenr); if (ret != len) { fprintf(stderr, "unable to zero fill device\n"); @@ -1487,7 +1497,7 @@ static int ext2_open_fs(struct btrfs_convert_context *cctx, const char *name) if (!(ext2_fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE)) { - fprintf(stderr, "filetype feature is missing\n"); + error("filetype feature is missing"); goto fail; } @@ -1516,6 +1526,9 @@ static int __ext2_add_one_block(ext2_filsys fs, char *bitmap, offset /= EXT2FS_CLUSTER_RATIO(fs); offset += group_nr * EXT2_CLUSTERS_PER_GROUP(fs->super); for (i = 0; i < EXT2_CLUSTERS_PER_GROUP(fs->super); i++) { + if ((i + offset) >= ext2fs_blocks_count(fs->super)) + break; + if (ext2fs_test_bit(i, bitmap)) { u64 start; @@ -2171,6 +2184,42 @@ static void ext2_copy_inode_item(struct btrfs_inode_item *dst, } memset(&dst->reserved, 0, sizeof(dst->reserved)); } +static int ext2_check_state(struct btrfs_convert_context *cctx) +{ + ext2_filsys fs = cctx->fs_data; + + if (!(fs->super->s_state & EXT2_VALID_FS)) + return 1; + else if (fs->super->s_state & EXT2_ERROR_FS) + return 1; + else + return 0; +} + +/* EXT2_*_FL to BTRFS_INODE_FLAG_* stringification helper */ +#define COPY_ONE_EXT2_FLAG(flags, ext2_inode, name) ({ \ + if (ext2_inode->i_flags & EXT2_##name##_FL) \ + flags |= BTRFS_INODE_##name; \ +}) + +/* + * Convert EXT2_*_FL to corresponding BTRFS_INODE_* flags + * + * Only a subset of EXT_*_FL is supported in btrfs. + */ +static void ext2_convert_inode_flags(struct btrfs_inode_item *dst, + struct ext2_inode *src) +{ + u64 flags = 0; + + COPY_ONE_EXT2_FLAG(flags, src, APPEND); + COPY_ONE_EXT2_FLAG(flags, src, SYNC); + COPY_ONE_EXT2_FLAG(flags, src, IMMUTABLE); + COPY_ONE_EXT2_FLAG(flags, src, NODUMP); + COPY_ONE_EXT2_FLAG(flags, src, NOATIME); + COPY_ONE_EXT2_FLAG(flags, src, DIRSYNC); + btrfs_set_stack_inode_flags(dst, flags); +} /* * copy a single inode. do all the required works, such as cloning @@ -2194,6 +2243,7 @@ static int ext2_copy_single_inode(struct btrfs_trans_handle *trans, BTRFS_INODE_NODATASUM; btrfs_set_stack_inode_flags(&btrfs_inode, flags); } + ext2_convert_inode_flags(&btrfs_inode, ext2_inode); switch (ext2_inode->i_mode & S_IFMT) { case S_IFREG: @@ -2289,6 +2339,7 @@ static const struct btrfs_convert_operations ext2_convert_ops = { .read_used_space = ext2_read_used_space, .copy_inodes = ext2_copy_inodes, .close_fs = ext2_close_fs, + .check_state = ext2_check_state, }; #endif @@ -2315,7 +2366,7 @@ static int convert_open_fs(const char *devname, } } - fprintf(stderr, "No file system found to convert.\n"); + error("no file system found to convert"); return -1; } @@ -2340,6 +2391,10 @@ static int do_convert(const char *devname, int datacsum, int packing, ret = convert_open_fs(devname, &cctx); if (ret) goto fail; + ret = convert_check_state(&cctx); + if (ret) + warning( + "source filesystem is not clean, running filesystem check is recommended"); ret = convert_read_used_space(&cctx); if (ret) goto fail; @@ -2376,7 +2431,7 @@ static int do_convert(const char *devname, int datacsum, int packing, memset(mkfs_cfg.chunk_uuid, 0, BTRFS_UUID_UNPARSED_SIZE); memset(mkfs_cfg.fs_uuid, 0, BTRFS_UUID_UNPARSED_SIZE); - ret = make_btrfs(fd, &mkfs_cfg, &cctx); + ret = make_convert_btrfs(fd, &mkfs_cfg, &cctx); if (ret) { error("unable to create initial ctree: %s", strerror(-ret)); goto fail; @@ -2465,7 +2520,7 @@ static int do_convert(const char *devname, int datacsum, int packing, * If this step succeed, we get a mountable btrfs. Otherwise * the source fs is left unchanged. */ - ret = migrate_super_block(fd, mkfs_cfg.super_bytenr, blocksize); + ret = migrate_super_block(fd, mkfs_cfg.super_bytenr); if (ret) { error("unable to migrate super block: %d", ret); goto fail; @@ -2586,7 +2641,7 @@ static int may_rollback(struct btrfs_root *root) num_stripes = multi->num_stripes; physical = multi->stripes[0].physical; - kfree(multi); + free(multi); if (num_stripes != 1) { error("num stripes for bytenr %llu is not 1", bytenr); @@ -2727,7 +2782,7 @@ static int do_rollback(const char *devname) key.objectid = objectid; key.offset = 0; - btrfs_set_key_type(&key, BTRFS_EXTENT_DATA_KEY); + key.type = BTRFS_EXTENT_DATA_KEY; ret = btrfs_search_slot(NULL, image_root, &key, &path, 0, 0); if (ret != 0) { error("unable to find first file extent: %d", ret); @@ -2747,7 +2802,7 @@ static int do_rollback(const char *devname) btrfs_item_key_to_cpu(leaf, &key, path.slots[0]); if (key.objectid != objectid || key.offset != offset || - btrfs_key_type(&key) != BTRFS_EXTENT_DATA_KEY) + key.type != BTRFS_EXTENT_DATA_KEY) break; fi = btrfs_item_ptr(leaf, path.slots[0], @@ -2810,7 +2865,10 @@ next_extent: /* force no allocation from system block group */ root->fs_info->system_allocs = -1; trans = btrfs_start_transaction(root, 1); - BUG_ON(!trans); + if (!trans) { + error("unable to start transaction"); + goto fail; + } /* * recow the whole chunk tree, this will remove all chunk tree blocks * from system block group @@ -2859,7 +2917,10 @@ next_extent: } ret = btrfs_commit_transaction(trans, root); - BUG_ON(ret); + if (ret) { + error("transaction commit failed: %d", ret); + goto fail; + } ret = close_ctree(root); if (ret) { @@ -2977,7 +3038,7 @@ static void print_usage(void) printf("\t-O|--features LIST comma separated list of filesystem features\n"); printf("\t--no-progress show only overview, not the detailed progress\n"); printf("\n"); - printf("Suported filesystems:\n"); + printf("Supported filesystems:\n"); printf("\text2/3/4: %s\n", BTRFSCONVERT_EXT2 ? "yes" : "no"); } @@ -3037,8 +3098,8 @@ int main(int argc, char *argv[]) case 'l': copylabel = -1; if (strlen(optarg) >= BTRFS_LABEL_SIZE) { - fprintf(stderr, - "WARNING: label too long, trimmed to %d bytes\n", + warning( + "label too long, trimmed to %d bytes", BTRFS_LABEL_SIZE - 1); } __strncpy_null(fslabel, optarg, BTRFS_LABEL_SIZE - 1); @@ -3055,8 +3116,7 @@ int main(int argc, char *argv[]) tmp = btrfs_parse_fs_features(tmp, &features); if (tmp) { - fprintf(stderr, - "Unrecognized filesystem feature '%s'\n", + error("unrecognized filesystem feature: %s", tmp); free(orig); exit(1); @@ -3072,8 +3132,7 @@ int main(int argc, char *argv[]) btrfs_parse_features_to_string(buf, features & ~BTRFS_CONVERT_ALLOWED_FEATURES); - fprintf(stderr, - "ERROR: features not allowed for convert: %s\n", + error("features not allowed for convert: %s", buf); exit(1); } @@ -3109,11 +3168,10 @@ int main(int argc, char *argv[]) file = argv[optind]; ret = check_mounted(file); if (ret < 0) { - fprintf(stderr, "Could not check mount status: %s\n", - strerror(-ret)); + error("could not check mount status: %s", strerror(-ret)); return 1; } else if (ret) { - fprintf(stderr, "%s is mounted\n", file); + error("%s is mounted", file); return 1; } @@ -2580,7 +2580,9 @@ int btrfs_insert_item(struct btrfs_trans_handle *trans, struct btrfs_root unsigned long ptr; path = btrfs_alloc_path(); - BUG_ON(!path); + if (!path) + return -ENOMEM; + ret = btrfs_insert_empty_item(trans, root, path, cpu_key, data_size); if (!ret) { leaf = path->nodes[0]; @@ -85,6 +85,9 @@ struct btrfs_free_space_ctl; /* 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 @@ -464,6 +467,14 @@ struct btrfs_super_block { * ones specified below then we will fail to mount */ #define BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE (1ULL << 0) +/* + * Older kernels on big-endian systems produced broken free space tree bitmaps, + * and btrfs-progs also used to corrupt the free space tree. 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. + */ +#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) @@ -490,6 +501,11 @@ struct btrfs_super_block { #define BTRFS_FEATURE_COMPAT_SUPP 0ULL +/* + * The FREE_SPACE_TREE and FREE_SPACE_TREE_VALID compat_ro bits must not be + * added here until read-write support for the free space tree is implemented in + * btrfs-progs. + */ #define BTRFS_FEATURE_COMPAT_RO_SUPP 0ULL #define BTRFS_FEATURE_INCOMPAT_SUPP \ @@ -552,18 +568,20 @@ struct btrfs_node { struct btrfs_path { struct extent_buffer *nodes[BTRFS_MAX_LEVEL]; int slots[BTRFS_MAX_LEVEL]; - /* if there is real range locking, this locks field will change */ +#if 0 + /* The kernel locking sheme is not done in userspace. */ int locks[BTRFS_MAX_LEVEL]; - int reada; +#endif + signed char reada; /* keep some upper locks as we walk down */ - int lowest_level; + u8 lowest_level; /* * set by btrfs_split_item, tells search_slot to keep all locks * and to force calls to keep space in the nodes */ - unsigned int search_for_split:1; - unsigned int skip_check_block:1; + u8 search_for_split; + u8 skip_check_block; }; /* @@ -785,6 +803,84 @@ struct btrfs_root_ref { __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 @@ -838,6 +934,14 @@ struct btrfs_file_extent_item { } __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__)); + struct btrfs_csum_item { u8 csum; } __attribute__ ((__packed__)); @@ -1073,7 +1177,6 @@ struct btrfs_root { u32 type; - u64 highest_inode; u64 last_inode_alloc; /* @@ -1211,10 +1314,42 @@ struct btrfs_root { #define BTRFS_QGROUP_RELATION_KEY 246 /* - * Persistently stores the io stats in the device tree. - * One key for all stats, (0, BTRFS_DEV_STATS_KEY, devid). + * 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_DEV_STATS_KEY 249 +#define BTRFS_PERSISTENT_ITEM_KEY 249 /* * Persistently stores the device replace state in the device tree. @@ -1246,6 +1381,15 @@ struct btrfs_root { #define BTRFS_INODE_NODATASUM (1 << 0) #define BTRFS_INODE_NODATACOW (1 << 1) #define BTRFS_INODE_READONLY (1 << 2) +#define BTRFS_INODE_NOCOMPRESS (1 << 3) +#define BTRFS_INODE_PREALLOC (1 << 4) +#define BTRFS_INODE_SYNC (1 << 5) +#define BTRFS_INODE_IMMUTABLE (1 << 6) +#define BTRFS_INODE_APPEND (1 << 7) +#define BTRFS_INODE_NODUMP (1 << 8) +#define BTRFS_INODE_NOATIME (1 << 9) +#define BTRFS_INODE_DIRSYNC (1 << 10) +#define BTRFS_INODE_COMPRESS (1 << 11) #define read_eb_member(eb, ptr, type, member, result) ( \ read_extent_buffer(eb, (char *)(result), \ @@ -1834,17 +1978,6 @@ static inline void btrfs_dir_item_key_to_cpu(struct extent_buffer *eb, btrfs_disk_key_to_cpu(key, &disk_key); } - -static inline u8 btrfs_key_type(struct btrfs_key *key) -{ - return key->type; -} - -static inline void btrfs_set_key_type(struct btrfs_key *key, u8 val) -{ - key->type = val; -} - /* struct btrfs_header */ BTRFS_SETGET_HEADER_FUNCS(header_bytenr, struct btrfs_header, bytenr, 64); BTRFS_SETGET_HEADER_FUNCS(header_generation, struct btrfs_header, @@ -2192,6 +2325,49 @@ BTRFS_SETGET_STACK_FUNCS(stack_qgroup_limit_rsv_referenced, BTRFS_SETGET_STACK_FUNCS(stack_qgroup_limit_rsv_exclusive, struct btrfs_qgroup_limit_item, rsv_exclusive, 64); +/* btrfs_balance_item */ +BTRFS_SETGET_FUNCS(balance_item_flags, struct btrfs_balance_item, flags, 64); + +static inline struct btrfs_disk_balance_args* btrfs_balance_item_data( + struct extent_buffer *eb, struct btrfs_balance_item *bi) +{ + unsigned long offset = (unsigned long)bi; + struct btrfs_balance_item *p; + p = (struct btrfs_balance_item *)(eb->data + offset); + return &p->data; +} + +static inline struct btrfs_disk_balance_args* btrfs_balance_item_meta( + struct extent_buffer *eb, struct btrfs_balance_item *bi) +{ + unsigned long offset = (unsigned long)bi; + struct btrfs_balance_item *p; + p = (struct btrfs_balance_item *)(eb->data + offset); + return &p->meta; +} + +static inline struct btrfs_disk_balance_args* btrfs_balance_item_sys( + struct extent_buffer *eb, struct btrfs_balance_item *bi) +{ + unsigned long offset = (unsigned long)bi; + struct btrfs_balance_item *p; + p = (struct btrfs_balance_item *)(eb->data + offset); + return &p->sys; +} + +/* + * btrfs_dev_stats_item helper, returns pointer to the raw array, do the + * endiannes conversion, @dsi is offset to eb data + */ +static inline __le64* btrfs_dev_stats_values(struct extent_buffer *eb, + struct btrfs_dev_stats_item *dsi) +{ + unsigned long offset = (unsigned long)dsi; + struct btrfs_dev_stats_item *p; + p = (struct btrfs_dev_stats_item *)(eb->data + offset); + return p->values; +} + /* * this returns the number of bytes used by the item on disk, minus the * size of any extent headers. If a file is compressed on disk, this is @@ -2264,14 +2440,20 @@ static inline u32 btrfs_level_size(struct btrfs_root *root, int level) { return root->nodesize; } -static inline int btrfs_fs_incompat(struct btrfs_fs_info *fs_info, u64 flag) +#define btrfs_fs_incompat(fs_info, opt) \ + __btrfs_fs_incompat((fs_info), BTRFS_FEATURE_INCOMPAT_##opt) + +static inline int __btrfs_fs_incompat(struct btrfs_fs_info *fs_info, u64 flag) { struct btrfs_super_block *disk_super; disk_super = fs_info->super_copy; return !!(btrfs_super_incompat_flags(disk_super) & flag); } -static inline int btrfs_fs_compat_ro(struct btrfs_fs_info *fs_info, u64 flag) +#define btrfs_fs_compat_ro(fs_info, opt) \ + __btrfs_fs_compat_ro((fs_info), BTRFS_FEATURE_COMPAT_RO_##opt) + +static inline int __btrfs_fs_compat_ro(struct btrfs_fs_info *fs_info, u64 flag) { struct btrfs_super_block *disk_super; disk_super = fs_info->super_copy; @@ -2327,6 +2509,10 @@ int btrfs_inc_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct extent_buffer *buf, int record_parent); int btrfs_dec_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct extent_buffer *buf, int record_parent); +int btrfs_free_tree_block(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct extent_buffer *buf, + u64 parent, int last_ref); int btrfs_free_extent(struct btrfs_trans_handle *trans, struct btrfs_root *root, u64 bytenr, u64 num_bytes, u64 parent, @@ -2487,6 +2673,8 @@ int btrfs_add_root_ref(struct btrfs_trans_handle *trans, int btrfs_insert_root(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_key *key, struct btrfs_root_item *item); +int btrfs_del_root(struct btrfs_trans_handle *trans, struct btrfs_root *root, + struct btrfs_key *key); int btrfs_update_root(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_key *key, struct btrfs_root_item *item); diff --git a/debian/changelog b/debian/changelog index d7424990..eb3767c0 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +btrfs-progs (4.9.1-1) UNRELEASED; urgency=medium + + * New upstream release Closes: #849353, #817806, #854915, #845473 + + -- Dimitri John Ledkov <xnox@ubuntu.com> Mon, 13 Feb 2017 11:21:54 +0000 + btrfs-progs (4.7.3-1) unstable; urgency=medium * New upstream release. @@ -75,7 +75,7 @@ int btrfs_insert_xattr_item(struct btrfs_trans_handle *trans, u32 data_size; key.objectid = dir; - btrfs_set_key_type(&key, BTRFS_XATTR_ITEM_KEY); + key.type = BTRFS_XATTR_ITEM_KEY; key.offset = btrfs_name_hash(name, name_len); path = btrfs_alloc_path(); if (!path) @@ -125,7 +125,7 @@ int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, struct btrfs_root u32 data_size; key.objectid = dir; - btrfs_set_key_type(&key, BTRFS_DIR_ITEM_KEY); + key.type = BTRFS_DIR_ITEM_KEY; key.offset = btrfs_name_hash(name, name_len); path = btrfs_alloc_path(); if (!path) @@ -156,7 +156,7 @@ int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, struct btrfs_root } btrfs_release_path(path); - btrfs_set_key_type(&key, BTRFS_DIR_INDEX_KEY); + key.type = BTRFS_DIR_INDEX_KEY; key.offset = index; dir_item = insert_with_overflow(trans, root, path, &key, data_size, name, name_len); @@ -196,7 +196,7 @@ struct btrfs_dir_item *btrfs_lookup_dir_item(struct btrfs_trans_handle *trans, struct extent_buffer *leaf; key.objectid = dir; - btrfs_set_key_type(&key, BTRFS_DIR_ITEM_KEY); + key.type = BTRFS_DIR_ITEM_KEY; key.offset = btrfs_name_hash(name, name_len); @@ -213,7 +213,7 @@ struct btrfs_dir_item *btrfs_lookup_dir_item(struct btrfs_trans_handle *trans, btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]); if (found_key.objectid != dir || - btrfs_key_type(&found_key) != BTRFS_DIR_ITEM_KEY || + found_key.type != BTRFS_DIR_ITEM_KEY || found_key.offset != key.offset) return NULL; @@ -87,7 +87,7 @@ static int ins_one(struct btrfs_trans_handle *trans, struct btrfs_root *root, inode_map.objectid = objectid; inode_map.flags = 0; - btrfs_set_key_type(&inode_map, BTRFS_INODE_ITEM_KEY); + inode_map.type = BTRFS_INODE_ITEM_KEY; inode_map.offset = 0; initial_inode_init(root, &inode_item); @@ -158,7 +158,7 @@ static int insert_dup(struct btrfs_trans_handle *trans, struct btrfs_root key.objectid = file_oid; key.flags = 0; - btrfs_set_key_type(&key, BTRFS_INODE_ITEM_KEY); + key.type = BTRFS_INODE_ITEM_KEY; key.offset = 0; ret = btrfs_insert_dir_item(trans, root, buf, strlen(buf), dir_oid, &key, BTRFS_FT_UNKNOWN); @@ -312,7 +312,7 @@ static int empty_tree(struct btrfs_trans_handle *trans, struct btrfs_root key.offset = (u64)-1; key.flags = 0; - btrfs_set_key_type(&key, BTRFS_DIR_ITEM_KEY); + key.type = BTRFS_DIR_ITEM_KEY; key.objectid = dir_oid; while(nr-- >= 0) { btrfs_init_path(&path); @@ -123,7 +123,7 @@ u32 btrfs_csum_data(struct btrfs_root *root, char *data, u32 seed, size_t len) return crc32c(seed, data, len); } -void btrfs_csum_final(u32 crc, char *result) +void btrfs_csum_final(u32 crc, u8 *result) { put_unaligned_le32(~crc, result); } @@ -131,7 +131,7 @@ void btrfs_csum_final(u32 crc, char *result) static int __csum_tree_block_size(struct extent_buffer *buf, u16 csum_size, int verify, int silent) { - char result[BTRFS_CSUM_SIZE]; + u8 result[BTRFS_CSUM_SIZE]; u32 len; u32 crc = ~(u32)0; @@ -241,7 +241,7 @@ static int verify_parent_transid(struct extent_io_tree *io_tree, ret = 1; out: - clear_extent_buffer_uptodate(io_tree, eb); + clear_extent_buffer_uptodate(eb); return ret; } @@ -326,16 +326,17 @@ struct extent_buffer* read_tree_block_fs_info( * Such unaligned tree block will free overlapping extent buffer, * causing use-after-free bugs for fuzzed images. */ - if (!IS_ALIGNED(bytenr, sectorsize)) { + if (bytenr < sectorsize || !IS_ALIGNED(bytenr, sectorsize)) { error("tree block bytenr %llu is not aligned to sectorsize %u", bytenr, sectorsize); return ERR_PTR(-EIO); } - if (!IS_ALIGNED(blocksize, nodesize)) { + if (blocksize < nodesize || !IS_ALIGNED(blocksize, nodesize)) { error("tree block size %u is not aligned to nodesize %u", blocksize, nodesize); return ERR_PTR(-EIO); } + eb = btrfs_find_create_tree_block(fs_info, bytenr, blocksize); if (!eb) return ERR_PTR(-ENOMEM); @@ -477,7 +478,7 @@ int write_tree_block(struct btrfs_trans_handle *trans, return write_and_map_eb(trans, root, eb); } -int __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize, +void btrfs_setup_root(u32 nodesize, u32 leafsize, u32 sectorsize, u32 stripesize, struct btrfs_root *root, struct btrfs_fs_info *fs_info, u64 objectid) { @@ -493,7 +494,6 @@ int __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize, root->fs_info = fs_info; root->objectid = objectid; root->last_trans = 0; - root->highest_inode = 0; root->last_inode_alloc = 0; INIT_LIST_HEAD(&root->dirty_list); @@ -501,7 +501,6 @@ int __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize, memset(&root->root_key, 0, sizeof(root->root_key)); memset(&root->root_item, 0, sizeof(root->root_item)); root->root_key.objectid = objectid; - return 0; } static int update_cowonly_root(struct btrfs_trans_handle *trans, @@ -634,7 +633,7 @@ static int find_and_setup_root(struct btrfs_root *tree_root, u32 blocksize; u64 generation; - __setup_root(tree_root->nodesize, tree_root->leafsize, + btrfs_setup_root(tree_root->nodesize, tree_root->leafsize, tree_root->sectorsize, tree_root->stripesize, root, fs_info, objectid); ret = btrfs_find_last_root(tree_root, objectid, @@ -670,7 +669,7 @@ static int find_and_setup_log_root(struct btrfs_root *tree_root, blocksize = tree_root->nodesize; - __setup_root(tree_root->nodesize, tree_root->leafsize, + btrfs_setup_root(tree_root->nodesize, tree_root->leafsize, tree_root->sectorsize, tree_root->stripesize, log_root, fs_info, BTRFS_TREE_LOG_OBJECTID); @@ -734,12 +733,16 @@ struct btrfs_root *btrfs_read_fs_root_no_cache(struct btrfs_fs_info *fs_info, goto insert; } - __setup_root(tree_root->nodesize, tree_root->leafsize, + btrfs_setup_root(tree_root->nodesize, tree_root->leafsize, tree_root->sectorsize, tree_root->stripesize, root, fs_info, location->objectid); path = btrfs_alloc_path(); - BUG_ON(!path); + if (!path) { + free(root); + return ERR_PTR(-ENOMEM); + } + ret = btrfs_search_slot(NULL, tree_root, location, path, 0, 0); if (ret != 0) { if (ret > 0) @@ -900,7 +903,8 @@ free_all: return NULL; } -int btrfs_check_fs_compatibility(struct btrfs_super_block *sb, int writable) +int btrfs_check_fs_compatibility(struct btrfs_super_block *sb, + unsigned int flags) { u64 features; @@ -919,13 +923,22 @@ int btrfs_check_fs_compatibility(struct btrfs_super_block *sb, int writable) btrfs_set_super_incompat_flags(sb, features); } - features = btrfs_super_compat_ro_flags(sb) & - ~BTRFS_FEATURE_COMPAT_RO_SUPP; - if (writable && features) { - printk("couldn't open RDWR because of unsupported " - "option features (%Lx).\n", - (unsigned long long)features); - return -ENOTSUP; + features = btrfs_super_compat_ro_flags(sb); + if (flags & OPEN_CTREE_WRITES) { + if (flags & OPEN_CTREE_INVALIDATE_FST) { + /* Clear the FREE_SPACE_TREE_VALID bit on disk... */ + features &= ~BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE_VALID; + btrfs_set_super_compat_ro_flags(sb, features); + /* ... and ignore the free space tree bit. */ + features &= ~BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE; + } + if (features & ~BTRFS_FEATURE_COMPAT_RO_SUPP) { + printk("couldn't open RDWR because of unsupported " + "option features (%Lx).\n", + (unsigned long long)features); + return -ENOTSUP; + } + } return 0; } @@ -972,7 +985,7 @@ static int setup_root_or_create_block(struct btrfs_fs_info *fs_info, btrfs_find_create_tree_block(fs_info, 0, nodesize); if (!info_root->node) return -ENOMEM; - clear_extent_buffer_uptodate(NULL, info_root->node); + clear_extent_buffer_uptodate(info_root->node); } return 0; @@ -998,7 +1011,7 @@ int btrfs_setup_all_roots(struct btrfs_fs_info *fs_info, u64 root_tree_bytenr, stripesize = btrfs_super_stripesize(sb); root = fs_info->tree_root; - __setup_root(nodesize, leafsize, sectorsize, stripesize, + btrfs_setup_root(nodesize, leafsize, sectorsize, stripesize, root, fs_info, BTRFS_ROOT_TREE_OBJECTID); blocksize = root->nodesize; generation = btrfs_super_generation(sb); @@ -1049,7 +1062,7 @@ int btrfs_setup_all_roots(struct btrfs_fs_info *fs_info, u64 root_tree_bytenr, if (ret == 0) fs_info->quota_enabled = 1; - if (btrfs_fs_compat_ro(fs_info, BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE)) { + if (btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE)) { ret = find_and_setup_root(root, fs_info, BTRFS_FREE_SPACE_TREE_OBJECTID, fs_info->free_space_root); if (ret) { @@ -1163,7 +1176,7 @@ int btrfs_scan_fs_devices(int fd, const char *path, } if (!skip_devices && total_devs != 1) { - ret = btrfs_scan_lblkid(); + ret = btrfs_scan_devices(); if (ret) return ret; } @@ -1187,7 +1200,7 @@ int btrfs_setup_chunk_tree_and_device_map(struct btrfs_fs_info *fs_info, sectorsize = btrfs_super_sectorsize(sb); stripesize = btrfs_super_stripesize(sb); - __setup_root(nodesize, leafsize, sectorsize, stripesize, + btrfs_setup_root(nodesize, leafsize, sectorsize, stripesize, fs_info->chunk_root, fs_info, BTRFS_CHUNK_TREE_OBJECTID); ret = btrfs_read_sys_array(fs_info->chunk_root); @@ -1316,8 +1329,7 @@ static struct btrfs_fs_info *__open_ctree_fd(int fp, const char *path, memcpy(fs_info->fsid, &disk_super->fsid, BTRFS_FSID_SIZE); - ret = btrfs_check_fs_compatibility(fs_info->super_copy, - flags & OPEN_CTREE_WRITES); + ret = btrfs_check_fs_compatibility(fs_info->super_copy, flags); if (ret) goto out_devices; @@ -1407,7 +1419,11 @@ struct btrfs_root *open_ctree_fd(int fp, const char *path, u64 sb_bytenr, struct btrfs_fs_info *info; /* This flags may not return fs_info with any valid root */ - BUG_ON(flags & OPEN_CTREE_IGNORE_CHUNK_TREE_ERROR); + if (flags & OPEN_CTREE_IGNORE_CHUNK_TREE_ERROR) { + error("invalid open_ctree flags: 0x%llx", + (unsigned long long)flags); + return NULL; + } info = __open_ctree_fd(fp, path, sb_bytenr, 0, 0, flags); if (!info) return NULL; @@ -1425,7 +1441,7 @@ struct btrfs_root *open_ctree_fd(int fp, const char *path, u64 sb_bytenr, */ static int check_super(struct btrfs_super_block *sb, unsigned sbflags) { - char result[BTRFS_CSUM_SIZE]; + u8 result[BTRFS_CSUM_SIZE]; u32 crc; u16 csum_type; int csum_size; @@ -1553,7 +1569,7 @@ static int check_super(struct btrfs_super_block *sb, unsigned sbflags) } if (btrfs_super_sys_array_size(sb) < sizeof(struct btrfs_disk_key) + sizeof(struct btrfs_chunk)) { - error("system chunk array too small %u < %lu", + error("system chunk array too small %u < %zu", btrfs_super_sys_array_size(sb), sizeof(struct btrfs_disk_key) + sizeof(struct btrfs_chunk)); @@ -1582,14 +1598,20 @@ int btrfs_read_dev_super(int fd, struct btrfs_super_block *sb, u64 sb_bytenr, if (sb_bytenr != BTRFS_SUPER_INFO_OFFSET) { ret = pread64(fd, buf, BTRFS_SUPER_INFO_SIZE, sb_bytenr); + /* real error */ + if (ret < 0) + return -errno; + + /* Not large enough sb, return -ENOENT instead of normal -EIO */ if (ret < BTRFS_SUPER_INFO_SIZE) - return -1; + return -ENOENT; if (btrfs_super_bytenr(buf) != sb_bytenr) - return -1; + return -EIO; - if (check_super(buf, sbflags)) - return -1; + ret = check_super(buf, sbflags); + if (ret < 0) + return ret; memcpy(sb, buf, BTRFS_SUPER_INFO_SIZE); return 0; } @@ -1649,7 +1671,7 @@ static int write_dev_supers(struct btrfs_root *root, crc = ~(u32)0; crc = btrfs_csum_data(NULL, (char *)sb + BTRFS_CSUM_SIZE, crc, BTRFS_SUPER_INFO_SIZE - BTRFS_CSUM_SIZE); - btrfs_csum_final(crc, (char *)&sb->csum[0]); + btrfs_csum_final(crc, &sb->csum[0]); /* * super_copy is BTRFS_SUPER_INFO_SIZE bytes and is @@ -1673,7 +1695,7 @@ static int write_dev_supers(struct btrfs_root *root, crc = ~(u32)0; crc = btrfs_csum_data(NULL, (char *)sb + BTRFS_CSUM_SIZE, crc, BTRFS_SUPER_INFO_SIZE - BTRFS_CSUM_SIZE); - btrfs_csum_final(crc, (char *)&sb->csum[0]); + btrfs_csum_final(crc, &sb->csum[0]); /* * super_copy is BTRFS_SUPER_INFO_SIZE bytes and is @@ -74,6 +74,12 @@ enum btrfs_open_ctree_flags { /* Allow to open a partially created filesystem */ OPEN_CTREE_FS_PARTIAL = (1U << 12), + + /* + * Invalidate the free space tree (i.e., clear the FREE_SPACE_TREE_VALID + * compat_ro bit). + */ + OPEN_CTREE_INVALIDATE_FST = (1U << 13), }; /* @@ -120,7 +126,7 @@ void readahead_tree_block(struct btrfs_root *root, u64 bytenr, u32 blocksize, struct extent_buffer* btrfs_find_create_tree_block( struct btrfs_fs_info *fs_info, u64 bytenr, u32 blocksize); -int __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize, +void btrfs_setup_root(u32 nodesize, u32 leafsize, u32 sectorsize, u32 stripesize, struct btrfs_root *root, struct btrfs_fs_info *fs_info, u64 objectid); int clean_tree_block(struct btrfs_trans_handle *trans, @@ -128,7 +134,8 @@ int clean_tree_block(struct btrfs_trans_handle *trans, void btrfs_free_fs_info(struct btrfs_fs_info *fs_info); struct btrfs_fs_info *btrfs_new_fs_info(int writable, u64 sb_bytenr); -int btrfs_check_fs_compatibility(struct btrfs_super_block *sb, int writable); +int btrfs_check_fs_compatibility(struct btrfs_super_block *sb, + unsigned int flags); int btrfs_setup_all_roots(struct btrfs_fs_info *fs_info, u64 root_tree_bytenr, unsigned flags); void btrfs_release_all_roots(struct btrfs_fs_info *fs_info); @@ -175,7 +182,7 @@ int btrfs_set_buffer_uptodate(struct extent_buffer *buf); int wait_on_tree_block_writeback(struct btrfs_root *root, struct extent_buffer *buf); u32 btrfs_csum_data(struct btrfs_root *root, char *data, u32 seed, size_t len); -void btrfs_csum_final(u32 crc, char *result); +void btrfs_csum_final(u32 crc, u8 *result); int btrfs_commit_transaction(struct btrfs_trans_handle *trans, struct btrfs_root *root); @@ -190,7 +197,8 @@ int write_tree_block(struct btrfs_trans_handle *trans, int write_and_map_eb(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct extent_buffer *eb); -/* raid6.c */ +/* raid56.c */ void raid6_gen_syndrome(int disks, size_t bytes, void **ptrs); +int raid5_gen_result(int nr_devs, size_t stripe_len, int dest, void **data); #endif diff --git a/extent-tree.c b/extent-tree.c index 0607be66..b2847ff9 100644 --- a/extent-tree.c +++ b/extent-tree.c @@ -995,8 +995,7 @@ static int lookup_inline_extent_backref(struct btrfs_trans_handle *trans, int ret; int err = 0; int skinny_metadata = - btrfs_fs_incompat(root->fs_info, - BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA); + btrfs_fs_incompat(root->fs_info, SKINNY_METADATA); key.objectid = bytenr; key.type = BTRFS_EXTENT_ITEM_KEY; @@ -1456,8 +1455,7 @@ int btrfs_lookup_extent_info(struct btrfs_trans_handle *trans, u64 extent_flags; if (metadata && - !btrfs_fs_incompat(root->fs_info, - BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA)) { + !btrfs_fs_incompat(root->fs_info, SKINNY_METADATA)) { offset = root->nodesize; metadata = 0; } @@ -1552,8 +1550,7 @@ int btrfs_set_block_flags(struct btrfs_trans_handle *trans, struct btrfs_extent_item *item; u32 item_size; int skinny_metadata = - btrfs_fs_incompat(root->fs_info, - BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA); + btrfs_fs_incompat(root->fs_info, SKINNY_METADATA); path = btrfs_alloc_path(); if (!path) @@ -1664,7 +1661,7 @@ static int __btrfs_mod_ref(struct btrfs_trans_handle *trans, cond_resched(); if (level == 0) { btrfs_item_key_to_cpu(buf, &key, i); - if (btrfs_key_type(&key) != BTRFS_EXTENT_DATA_KEY) + if (key.type != BTRFS_EXTENT_DATA_KEY) continue; fi = btrfs_item_ptr(buf, i, struct btrfs_file_extent_item); @@ -2079,8 +2076,7 @@ static int finish_current_insert(struct btrfs_trans_handle *trans, struct btrfs_key key; int ret; int skinny_metadata = - btrfs_fs_incompat(extent_root->fs_info, - BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA); + btrfs_fs_incompat(extent_root->fs_info, SKINNY_METADATA); while(1) { ret = find_first_extent_bit(&info->extent_ins, 0, &start, @@ -2193,8 +2189,7 @@ static int __free_extent(struct btrfs_trans_handle *trans, u32 item_size; u64 refs; int skinny_metadata = - btrfs_fs_incompat(extent_root->fs_info, - BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA); + btrfs_fs_incompat(extent_root->fs_info, SKINNY_METADATA); if (root->fs_info->free_extent_hook) { root->fs_info->free_extent_hook(trans, root, bytenr, num_bytes, @@ -2467,6 +2462,17 @@ static int del_pending_extents(struct btrfs_trans_handle *trans, struct return err; } + +int btrfs_free_tree_block(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct extent_buffer *buf, + u64 parent, int last_ref) +{ + return btrfs_free_extent(trans, root, buf->start, buf->len, parent, + root->root_key.objectid, + btrfs_header_level(buf), 0); +} + /* * remove an extent from the root, returns 0 on success */ @@ -2538,7 +2544,7 @@ static int noinline find_free_extent(struct btrfs_trans_handle *trans, int wrapped = 0; WARN_ON(num_bytes < root->sectorsize); - btrfs_set_key_type(ins, BTRFS_EXTENT_ITEM_KEY); + ins->type = BTRFS_EXTENT_ITEM_KEY; search_start = stripe_align(root, search_start); @@ -2606,11 +2612,20 @@ check_failed: } if (!(data & BTRFS_BLOCK_GROUP_DATA)) { - if (check_crossing_stripes(ins->objectid, num_bytes)) { - search_start = round_down(ins->objectid + num_bytes, - BTRFS_STRIPE_LEN); + if (check_crossing_stripes(info, ins->objectid, num_bytes)) { + struct btrfs_block_group_cache *bg_cache; + u64 bg_offset; + + bg_cache = btrfs_lookup_block_group(info, ins->objectid); + if (!bg_cache) + goto no_bg_cache; + bg_offset = ins->objectid - bg_cache->key.objectid; + + search_start = round_up(bg_offset + num_bytes, + BTRFS_STRIPE_LEN) + bg_offset; goto new_group; } +no_bg_cache: block_group = btrfs_lookup_block_group(info, ins->objectid); if (block_group) trans->block_group = block_group; @@ -2706,15 +2721,14 @@ static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans, struct btrfs_path *path; struct extent_buffer *leaf; u32 size = sizeof(*extent_item) + sizeof(*iref); - int skinny_metadata = - btrfs_fs_incompat(fs_info, - BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA); + int skinny_metadata = btrfs_fs_incompat(fs_info, SKINNY_METADATA); if (!skinny_metadata) size += sizeof(*block_info); path = btrfs_alloc_path(); - BUG_ON(!path); + if (!path) + return -ENOMEM; ret = btrfs_insert_empty_item(trans, fs_info->extent_root, path, ins, size); @@ -2779,8 +2793,7 @@ static int alloc_tree_block(struct btrfs_trans_handle *trans, set_state_private(&root->fs_info->extent_ins, ins->objectid, (unsigned long)extent_op); } else { - if (btrfs_fs_incompat(root->fs_info, - BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA)) { + if (btrfs_fs_incompat(root->fs_info, SKINNY_METADATA)) { ins->offset = level; ins->type = BTRFS_METADATA_ITEM_KEY; } @@ -3230,7 +3243,7 @@ int btrfs_read_block_groups(struct btrfs_root *root) root = info->extent_root; key.objectid = 0; key.offset = 0; - btrfs_set_key_type(&key, BTRFS_BLOCK_GROUP_ITEM_KEY); + key.type = BTRFS_BLOCK_GROUP_ITEM_KEY; path = btrfs_alloc_path(); if (!path) return -ENOMEM; @@ -3312,7 +3325,7 @@ btrfs_add_block_group(struct btrfs_fs_info *fs_info, u64 bytes_used, u64 type, cache->key.objectid = chunk_offset; cache->key.offset = size; - btrfs_set_key_type(&cache->key, BTRFS_BLOCK_GROUP_ITEM_KEY); + cache->key.type = BTRFS_BLOCK_GROUP_ITEM_KEY; btrfs_set_block_group_used(&cache->item, bytes_used); btrfs_set_block_group_chunk_objectid(&cache->item, chunk_objectid); cache->flags = type; @@ -3424,7 +3437,7 @@ int btrfs_make_block_groups(struct btrfs_trans_handle *trans, cache->key.objectid = cur_start; cache->key.offset = group_size; - btrfs_set_key_type(&cache->key, BTRFS_BLOCK_GROUP_ITEM_KEY); + cache->key.type = BTRFS_BLOCK_GROUP_ITEM_KEY; btrfs_set_block_group_used(&cache->item, 0); btrfs_set_block_group_chunk_objectid(&cache->item, @@ -3868,7 +3881,7 @@ int btrfs_fix_block_accounting(struct btrfs_trans_handle *trans, btrfs_init_path(&path); key.offset = 0; key.objectid = 0; - btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY); + key.type = BTRFS_EXTENT_ITEM_KEY; ret = btrfs_search_slot(trans, root->fs_info->extent_root, &key, &path, 0, 0); if (ret < 0) @@ -4060,7 +4073,7 @@ static int __btrfs_record_file_extent(struct btrfs_trans_handle *trans, btrfs_release_path(path); ins_key.objectid = objectid; ins_key.offset = file_pos; - btrfs_set_key_type(&ins_key, BTRFS_EXTENT_DATA_KEY); + ins_key.type = BTRFS_EXTENT_DATA_KEY; ret = btrfs_insert_empty_item(trans, root, path, &ins_key, sizeof(*fi)); if (ret) diff --git a/extent_io.h b/extent_io.h index 208c4fea..bd6cf9ef 100644 --- a/extent_io.h +++ b/extent_io.h @@ -125,8 +125,7 @@ static inline int set_extent_buffer_uptodate(struct extent_buffer *eb) return 0; } -static inline int clear_extent_buffer_uptodate(struct extent_io_tree *tree, - struct extent_buffer *eb) +static inline int clear_extent_buffer_uptodate(struct extent_buffer *eb) { eb->flags &= ~EXTENT_UPTODATE; return 0; diff --git a/file-item.c b/file-item.c index 7a3bbf35..e462b4bb 100644 --- a/file-item.c +++ b/file-item.c @@ -36,16 +36,29 @@ int btrfs_insert_file_extent(struct btrfs_trans_handle *trans, u64 disk_num_bytes, u64 num_bytes) { int ret = 0; + int is_hole = 0; struct btrfs_file_extent_item *item; struct btrfs_key file_key; struct btrfs_path *path; struct extent_buffer *leaf; + if (offset == 0) + is_hole = 1; + /* For NO_HOLES, we don't insert hole file extent */ + if (btrfs_fs_incompat(root->fs_info, NO_HOLES) && is_hole) + return 0; + + /* For hole, its disk_bytenr and disk_num_bytes must be 0 */ + if (is_hole) + disk_num_bytes = 0; + path = btrfs_alloc_path(); - BUG_ON(!path); + if (!path) + return -ENOMEM; + file_key.objectid = objectid; file_key.offset = pos; - btrfs_set_key_type(&file_key, BTRFS_EXTENT_DATA_KEY); + file_key.type = BTRFS_EXTENT_DATA_KEY; ret = btrfs_insert_empty_item(trans, root, path, &file_key, sizeof(*item)); @@ -90,7 +103,7 @@ int btrfs_insert_inline_extent(struct btrfs_trans_handle *trans, key.objectid = objectid; key.offset = offset; - btrfs_set_key_type(&key, BTRFS_EXTENT_DATA_KEY); + key.type = BTRFS_EXTENT_DATA_KEY; datasize = btrfs_file_extent_calc_inline_size(size); ret = btrfs_insert_empty_item(trans, root, path, &key, datasize); @@ -135,7 +148,7 @@ btrfs_lookup_csum(struct btrfs_trans_handle *trans, file_key.objectid = BTRFS_EXTENT_CSUM_OBJECTID; file_key.offset = bytenr; - btrfs_set_key_type(&file_key, BTRFS_EXTENT_CSUM_KEY); + file_key.type = BTRFS_EXTENT_CSUM_KEY; ret = btrfs_search_slot(trans, root, &file_key, path, 0, cow); if (ret < 0) goto fail; @@ -146,7 +159,7 @@ btrfs_lookup_csum(struct btrfs_trans_handle *trans, goto fail; path->slots[0]--; btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]); - if (btrfs_key_type(&found_key) != BTRFS_EXTENT_CSUM_KEY) + if (found_key.type != BTRFS_EXTENT_CSUM_KEY) goto fail; csum_offset = (bytenr - found_key.offset) / root->sectorsize; @@ -188,7 +201,8 @@ int btrfs_csum_file_block(struct btrfs_trans_handle *trans, btrfs_super_csum_size(root->fs_info->super_copy); path = btrfs_alloc_path(); - BUG_ON(!path); + if (!path) + return -ENOMEM; file_key.objectid = BTRFS_EXTENT_CSUM_OBJECTID; file_key.offset = bytenr; @@ -297,7 +311,7 @@ csum: csum_offset * csum_size); found: csum_result = btrfs_csum_data(root, data, csum_result, len); - btrfs_csum_final(csum_result, (char *)&csum_result); + btrfs_csum_final(csum_result, (u8 *)&csum_result); if (csum_result == 0) { printk("csum result is 0 for block %llu\n", (unsigned long long)bytenr); diff --git a/free-space-cache.c b/free-space-cache.c index 357d69e7..286b185e 100644 --- a/free-space-cache.c +++ b/free-space-cache.c @@ -25,6 +25,7 @@ #include "crc32c.h" #include "bitops.h" #include "internal.h" +#include "utils.h" /* * Kernel always uses PAGE_CACHE_SIZE for sectorsize, but we don't have @@ -210,7 +211,7 @@ static int io_ctl_check_crc(struct io_ctl *io_ctl, int index) io_ctl_map_page(io_ctl, 0); crc = crc32c(crc, io_ctl->orig + offset, io_ctl->root->sectorsize - offset); - btrfs_csum_final(crc, (char *)&crc); + btrfs_csum_final(crc, (u8 *)&crc); if (val != crc) { printk("btrfs: csum mismatch on free space cache\n"); io_ctl_unmap_page(io_ctl); @@ -877,3 +878,129 @@ next: prev = e; } } + +int btrfs_clear_free_space_cache(struct btrfs_fs_info *fs_info, + struct btrfs_block_group_cache *bg) +{ + struct btrfs_trans_handle *trans; + struct btrfs_root *tree_root = fs_info->tree_root; + struct btrfs_path path; + struct btrfs_key key; + struct btrfs_disk_key location; + struct btrfs_free_space_header *sc_header; + struct extent_buffer *node; + u64 ino; + int slot; + int ret; + + trans = btrfs_start_transaction(tree_root, 1); + if (IS_ERR(trans)) + return PTR_ERR(trans); + + btrfs_init_path(&path); + + key.objectid = BTRFS_FREE_SPACE_OBJECTID; + key.type = 0; + key.offset = bg->key.objectid; + + ret = btrfs_search_slot(trans, tree_root, &key, &path, -1, 1); + if (ret > 0) { + ret = 0; + goto out; + } + if (ret < 0) + goto out; + + node = path.nodes[0]; + slot = path.slots[0]; + sc_header = btrfs_item_ptr(node, slot, struct btrfs_free_space_header); + btrfs_free_space_key(node, sc_header, &location); + ino = location.objectid; + + /* Delete the free space header, as we have the ino to continue */ + ret = btrfs_del_item(trans, tree_root, &path); + if (ret < 0) { + error("failed to remove free space header for block group %llu: %d", + bg->key.objectid, ret); + goto out; + } + btrfs_release_path(&path); + + /* Iterate from the end of the free space cache inode */ + key.objectid = ino; + key.type = BTRFS_EXTENT_DATA_KEY; + key.offset = (u64)-1; + ret = btrfs_search_slot(trans, tree_root, &key, &path, -1, 1); + if (ret < 0) { + error("failed to locate free space cache extent for block group %llu: %d", + bg->key.objectid, ret); + goto out; + } + while (1) { + struct btrfs_file_extent_item *fi; + u64 disk_bytenr; + u64 disk_num_bytes; + + ret = btrfs_previous_item(tree_root, &path, ino, + BTRFS_EXTENT_DATA_KEY); + if (ret > 0) { + ret = 0; + break; + } + if (ret < 0) { + error( + "failed to locate free space cache extent for block group %llu: %d", + bg->key.objectid, ret); + goto out; + } + node = path.nodes[0]; + slot = path.slots[0]; + btrfs_item_key_to_cpu(node, &key, slot); + fi = btrfs_item_ptr(node, slot, struct btrfs_file_extent_item); + disk_bytenr = btrfs_file_extent_disk_bytenr(node, fi); + disk_num_bytes = btrfs_file_extent_disk_num_bytes(node, fi); + + ret = btrfs_free_extent(trans, tree_root, disk_bytenr, + disk_num_bytes, 0, tree_root->objectid, + ino, key.offset); + if (ret < 0) { + error("failed to remove backref for disk bytenr %llu: %d", + disk_bytenr, ret); + goto out; + } + ret = btrfs_del_item(trans, tree_root, &path); + if (ret < 0) { + error( + "failed to remove free space extent data for ino %llu offset %llu: %d", + ino, key.offset, ret); + goto out; + } + } + btrfs_release_path(&path); + + /* Now delete free space cache inode item */ + key.objectid = ino; + key.type = BTRFS_INODE_ITEM_KEY; + key.offset = 0; + + ret = btrfs_search_slot(trans, tree_root, &key, &path, -1, 1); + if (ret > 0) + warning("free space inode %llu not found, ignore", ino); + if (ret < 0) { + error( + "failed to locate free space cache inode %llu for block group %llu: %d", + ino, bg->key.objectid, ret); + goto out; + } + ret = btrfs_del_item(trans, tree_root, &path); + if (ret < 0) { + error( + "failed to delete free space cache inode %llu for block group %llu: %d", + ino, bg->key.objectid, ret); + } +out: + btrfs_release_path(&path); + if (!ret) + btrfs_commit_transaction(trans, tree_root); + return ret; +} diff --git a/free-space-cache.h b/free-space-cache.h index 9214077a..707fb6d3 100644 --- a/free-space-cache.h +++ b/free-space-cache.h @@ -59,4 +59,6 @@ void unlink_free_space(struct btrfs_free_space_ctl *ctl, struct btrfs_free_space *info); int btrfs_add_free_space(struct btrfs_free_space_ctl *ctl, u64 offset, u64 bytes); +int btrfs_clear_free_space_cache(struct btrfs_fs_info *fs_info, + struct btrfs_block_group_cache *bg); #endif diff --git a/free-space-tree.c b/free-space-tree.c index 3c7a2463..f3a51263 100644 --- a/free-space-tree.c +++ b/free-space-tree.c @@ -20,6 +20,7 @@ #include "disk-io.h" #include "free-space-cache.h" #include "free-space-tree.h" +#include "transaction.h" static struct btrfs_free_space_info * search_free_space_info(struct btrfs_trans_handle *trans, @@ -67,6 +68,91 @@ static int free_space_test_bit(struct btrfs_block_group_cache *block_group, return !!extent_buffer_test_bit(leaf, ptr, i); } +static int clear_free_space_tree(struct btrfs_trans_handle *trans, + struct btrfs_root *root) +{ + struct btrfs_path *path; + struct btrfs_key key; + int nr; + int ret; + + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + + key.objectid = 0; + key.type = 0; + key.offset = 0; + + while (1) { + ret = btrfs_search_slot(trans, root, &key, path, -1, 1); + if (ret < 0) + goto out; + + nr = btrfs_header_nritems(path->nodes[0]); + if (!nr) + break; + + path->slots[0] = 0; + ret = btrfs_del_items(trans, root, path, 0, nr); + if (ret) + goto out; + + btrfs_release_path(path); + } + + ret = 0; +out: + btrfs_free_path(path); + return ret; +} + +int btrfs_clear_free_space_tree(struct btrfs_fs_info *fs_info) +{ + struct btrfs_trans_handle *trans; + struct btrfs_root *tree_root = fs_info->tree_root; + struct btrfs_root *free_space_root = fs_info->free_space_root; + int ret; + u64 features; + + trans = btrfs_start_transaction(tree_root, 0); + if (IS_ERR(trans)) + return PTR_ERR(trans); + + features = btrfs_super_compat_ro_flags(fs_info->super_copy); + features &= ~(BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE_VALID | + BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE); + btrfs_set_super_compat_ro_flags(fs_info->super_copy, features); + fs_info->free_space_root = NULL; + + ret = clear_free_space_tree(trans, free_space_root); + if (ret) + goto abort; + + ret = btrfs_del_root(trans, tree_root, &free_space_root->root_key); + if (ret) + goto abort; + + list_del(&free_space_root->dirty_list); + + ret = clean_tree_block(trans, tree_root, free_space_root->node); + if (ret) + goto abort; + ret = btrfs_free_tree_block(trans, free_space_root, + free_space_root->node, 0, 1); + if (ret) + goto abort; + + free_extent_buffer(free_space_root->node); + free_extent_buffer(free_space_root->commit_root); + kfree(free_space_root); + + ret = btrfs_commit_transaction(trans, tree_root); + +abort: + return ret; +} + static int load_free_space_bitmaps(struct btrfs_fs_info *fs_info, struct btrfs_block_group_cache *block_group, struct btrfs_path *path, diff --git a/free-space-tree.h b/free-space-tree.h index 7529a468..4845f13e 100644 --- a/free-space-tree.h +++ b/free-space-tree.h @@ -19,6 +19,7 @@ #ifndef __BTRFS_FREE_SPACE_TREE_H__ #define __BTRFS_FREE_SPACE_TREE_H__ +int btrfs_clear_free_space_tree(struct btrfs_fs_info *fs_info); int load_free_space_tree(struct btrfs_fs_info *fs_info, struct btrfs_block_group_cache *block_group); @@ -25,4 +25,14 @@ static inline u64 btrfs_name_hash(const char *name, int len) { return crc32c((u32)~1, name, len); } + +/* + * Figure the key offset of an extended inode ref + */ +static inline u64 btrfs_extref_hash(u64 parent_objectid, const char *name, + int len) +{ + return (u64)btrfs_crc32c(parent_objectid, name, len); +} + #endif @@ -148,6 +148,8 @@ static void usage_command_group_internal(const struct cmd_group *grp, int full, usage_command_internal(cmd->usagestr, cmd->token, full, 1, cmd->flags & CMD_ALIAS, outf); + if (cmd->flags & CMD_ALIAS) + putchar('\n'); continue; } diff --git a/btrfs-image.c b/image/main.c index 953d368d..0158844b 100644 --- a/btrfs-image.c +++ b/image/main.c @@ -44,6 +44,8 @@ #define COMPRESS_NONE 0 #define COMPRESS_ZLIB 1 +#define MAX_WORKER_THREADS (32) + struct meta_cluster_item { __le64 bytenr; __le32 size; @@ -95,9 +97,12 @@ struct metadump_struct { struct btrfs_root *root; FILE *out; - struct meta_cluster *cluster; + union { + struct meta_cluster cluster; + char meta_cluster_bytes[BLOCK_SIZE]; + }; - pthread_t *threads; + pthread_t threads[MAX_WORKER_THREADS]; size_t num_threads; pthread_mutex_t mutex; pthread_cond_t cond; @@ -130,7 +135,7 @@ struct mdrestore_struct { FILE *in; FILE *out; - pthread_t *threads; + pthread_t threads[MAX_WORKER_THREADS]; size_t num_threads; pthread_mutex_t mutex; pthread_cond_t cond; @@ -163,7 +168,7 @@ static struct extent_buffer *alloc_dummy_eb(u64 bytenr, u32 size); static void csum_block(u8 *buf, size_t len) { - char result[BTRFS_CRC32_SIZE]; + u8 result[BTRFS_CRC32_SIZE]; u32 crc = ~(u32)0; crc = crc32c(crc, buf + BTRFS_CSUM_SIZE, len - BTRFS_CSUM_SIZE); btrfs_csum_final(crc, result); @@ -312,7 +317,7 @@ static u64 logical_to_physical(struct mdrestore_struct *mdres, u64 logical, entry = tree_search(&mdres->chunk_tree, &search.l, chunk_cmp, 1); if (!entry) { if (mdres->in != stdin) - printf("Couldn't find a chunk, using logical\n"); + warning("cannot find a chunk, using logical"); return logical; } fs_chunk = rb_entry(entry, struct fs_chunk, l); @@ -354,7 +359,7 @@ static char *find_collision(struct metadump_struct *md, char *name, val = malloc(sizeof(struct name)); if (!val) { - fprintf(stderr, "Couldn't sanitize name, enomem\n"); + error("cannot sanitize name, not enough memory"); free(name); return NULL; } @@ -365,7 +370,7 @@ static char *find_collision(struct metadump_struct *md, char *name, val->len = name_len; val->sub = malloc(name_len); if (!val->sub) { - fprintf(stderr, "Couldn't sanitize name, enomem\n"); + error("cannot sanitize name, not enough memory"); free(val); free(name); return NULL; @@ -404,8 +409,8 @@ static char *find_collision(struct metadump_struct *md, char *name, } if (!found) { - fprintf(stderr, "Couldn't find a collision for '%.*s', " - "generating normal garbage, it won't match indexes\n", + warning( +"cannot find a hash collision for '%.*s', generating garbage, it won't match indexes", val->len, val->val); for (i = 0; i < name_len; i++) { char c = rand_range(94) + 33; @@ -445,8 +450,7 @@ static void sanitize_dir_item(struct metadump_struct *md, struct extent_buffer * if (md->sanitize_names > 1) { buf = malloc(name_len); if (!buf) { - fprintf(stderr, "Couldn't sanitize name, " - "enomem\n"); + error("cannot sanitize name, not enough memory"); return; } read_extent_buffer(eb, buf, name_ptr, name_len); @@ -455,7 +459,7 @@ static void sanitize_dir_item(struct metadump_struct *md, struct extent_buffer * garbage = generate_garbage(name_len); } if (!garbage) { - fprintf(stderr, "Couldn't sanitize name, enomem\n"); + error("cannot sanitize name, not enough memory"); return; } write_extent_buffer(eb, garbage, name_ptr, name_len); @@ -500,8 +504,7 @@ static void sanitize_inode_ref(struct metadump_struct *md, if (md->sanitize_names > 1) { buf = malloc(len); if (!buf) { - fprintf(stderr, "Couldn't sanitize name, " - "enomem\n"); + error("cannot sanitize name, not enough memory"); return; } read_extent_buffer(eb, buf, name_ptr, len); @@ -511,7 +514,7 @@ static void sanitize_inode_ref(struct metadump_struct *md, } if (!garbage) { - fprintf(stderr, "Couldn't sanitize name, enomem\n"); + error("cannot sanitize name, not enough memory"); return; } write_extent_buffer(eb, garbage, name_ptr, len); @@ -543,11 +546,11 @@ static void sanitize_name(struct metadump_struct *md, u8 *dst, eb = alloc_dummy_eb(src->start, src->len); if (!eb) { - fprintf(stderr, "Couldn't sanitize name, no memory\n"); + error("cannot sanitize name, not enough memory"); return; } - memcpy(eb->data, dst, eb->len); + memcpy(eb->data, src->data, src->len); switch (key->type) { case BTRFS_DIR_ITEM_KEY: @@ -673,7 +676,7 @@ static void *dump_worker(void *data) async->bufsize = compressBound(async->size); async->buffer = malloc(async->bufsize); if (!async->buffer) { - fprintf(stderr, "Error allocating buffer\n"); + error("not enough memory for async buffer"); pthread_mutex_lock(&md->mutex); if (!md->error) md->error = -ENOMEM; @@ -705,7 +708,7 @@ static void meta_cluster_init(struct metadump_struct *md, u64 start) md->num_items = 0; md->num_ready = 0; - header = &md->cluster->header; + header = &md->cluster.header; header->magic = cpu_to_le64(HEADER_MAGIC); header->bytenr = cpu_to_le64(start); header->nritems = cpu_to_le32(0); @@ -738,8 +741,6 @@ static void metadump_destroy(struct metadump_struct *md, int num_threads) free(name->sub); free(name); } - free(md->threads); - free(md->cluster); } static int metadump_init(struct metadump_struct *md, struct btrfs_root *root, @@ -749,14 +750,6 @@ static int metadump_init(struct metadump_struct *md, struct btrfs_root *root, int i, ret = 0; memset(md, 0, sizeof(*md)); - md->cluster = calloc(1, BLOCK_SIZE); - if (!md->cluster) - return -ENOMEM; - md->threads = calloc(num_threads, sizeof(pthread_t)); - if (!md->threads) { - free(md->cluster); - return -ENOMEM; - } INIT_LIST_HEAD(&md->list); INIT_LIST_HEAD(&md->ordered); md->root = root; @@ -796,7 +789,7 @@ static int write_zero(FILE *out, size_t size) static int write_buffers(struct metadump_struct *md, u64 *next) { - struct meta_cluster_header *header = &md->cluster->header; + struct meta_cluster_header *header = &md->cluster.header; struct meta_cluster_item *item; struct async_work *async; u64 bytenr = 0; @@ -820,24 +813,23 @@ static int write_buffers(struct metadump_struct *md, u64 *next) } if (err) { - fprintf(stderr, "One of the threads errored out %s\n", - strerror(err)); + error("one of the threads failed: %s", strerror(-err)); goto out; } /* setup and write index block */ list_for_each_entry(async, &md->ordered, ordered) { - item = md->cluster->items + nritems; + item = &md->cluster.items[nritems]; item->bytenr = cpu_to_le64(async->start); item->size = cpu_to_le32(async->bufsize); nritems++; } header->nritems = cpu_to_le32(nritems); - ret = fwrite(md->cluster, BLOCK_SIZE, 1, md->out); + ret = fwrite(&md->cluster, BLOCK_SIZE, 1, md->out); if (ret != 1) { - fprintf(stderr, "Error writing out cluster: %d\n", errno); - return -EIO; + error("unable to write out cluster: %s", strerror(errno)); + return -errno; } /* write buffers */ @@ -852,10 +844,10 @@ static int write_buffers(struct metadump_struct *md, u64 *next) ret = fwrite(async->buffer, async->bufsize, 1, md->out); if (ret != 1) { - err = -EIO; + error("unable to write out cluster: %s", + strerror(errno)); + err = -errno; ret = 0; - fprintf(stderr, "Error writing out cluster: %d\n", - errno); } free(async->buffer); @@ -869,9 +861,9 @@ static int write_buffers(struct metadump_struct *md, u64 *next) bytenr += size; ret = write_zero(md->out, size); if (ret != 1) { - fprintf(stderr, "Error zeroing out buffer: %d\n", - errno); - err = -EIO; + error("unable to zero out buffer: %s", + strerror(errno)); + err = -errno; } } out: @@ -927,7 +919,7 @@ static int flush_pending(struct metadump_struct *md, int done) struct async_work *async = NULL; struct extent_buffer *eb; u64 blocksize = md->root->nodesize; - u64 start; + u64 start = 0; u64 size; size_t offset; int ret = 0; @@ -969,8 +961,10 @@ static int flush_pending(struct metadump_struct *md, int done) if (ret < size) { free(async->buffer); free(async); - fprintf(stderr, "Error reading superblock\n"); - return -EIO; + error("unable to read superblock at %llu: %s", + (unsigned long long)start, + strerror(errno)); + return -errno; } size = 0; ret = 0; @@ -982,8 +976,8 @@ static int flush_pending(struct metadump_struct *md, int done) if (!extent_buffer_uptodate(eb)) { free(async->buffer); free(async); - fprintf(stderr, - "Error reading metadata block\n"); + error("unable to read metadata block %llu", + (unsigned long long)start); return -EIO; } copy_buffer(md, async->buffer + offset, eb); @@ -1013,8 +1007,7 @@ static int flush_pending(struct metadump_struct *md, int done) if (md->num_items >= ITEMS_PER_CLUSTER || done) { ret = write_buffers(md, &start); if (ret) - fprintf(stderr, "Error writing buffers %d\n", - errno); + error("unable to write buffers: %s", strerror(-ret)); else meta_cluster_init(md, start); } @@ -1091,7 +1084,8 @@ static int copy_tree_blocks(struct btrfs_root *root, struct extent_buffer *eb, ret = add_extent(btrfs_header_bytenr(eb), root->nodesize, metadump, 0); if (ret) { - fprintf(stderr, "Error adding metadata block\n"); + error("unable to add metadata block %llu: %d", + btrfs_header_bytenr(eb), ret); return ret; } @@ -1109,8 +1103,7 @@ static int copy_tree_blocks(struct btrfs_root *root, struct extent_buffer *eb, bytenr = btrfs_disk_root_bytenr(eb, ri); tmp = read_tree_block(root, bytenr, root->nodesize, 0); if (!extent_buffer_uptodate(tmp)) { - fprintf(stderr, - "Error reading log root block\n"); + error("unable to read log root block"); return -EIO; } ret = copy_tree_blocks(root, tmp, metadump, 0); @@ -1121,7 +1114,7 @@ static int copy_tree_blocks(struct btrfs_root *root, struct extent_buffer *eb, bytenr = btrfs_node_blockptr(eb, i); tmp = read_tree_block(root, bytenr, root->nodesize, 0); if (!extent_buffer_uptodate(tmp)) { - fprintf(stderr, "Error reading log block\n"); + error("unable to read log root block"); return -EIO; } ret = copy_tree_blocks(root, tmp, metadump, root_tree); @@ -1145,7 +1138,7 @@ static int copy_log_trees(struct btrfs_root *root, if (!root->fs_info->log_root_tree || !root->fs_info->log_root_tree->node) { - fprintf(stderr, "Error copying tree log, it wasn't setup\n"); + error("unable to copy tree log, it has not been setup"); return -EIO; } @@ -1171,8 +1164,7 @@ static int copy_space_cache(struct btrfs_root *root, ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); if (ret < 0) { - fprintf(stderr, "Error searching for free space inode %d\n", - ret); + error("free space inode not found: %d", ret); return ret; } @@ -1182,8 +1174,7 @@ static int copy_space_cache(struct btrfs_root *root, if (path->slots[0] >= btrfs_header_nritems(leaf)) { ret = btrfs_next_leaf(root, path); if (ret < 0) { - fprintf(stderr, "Error going to next leaf " - "%d\n", ret); + error("cannot go to next leaf %d", ret); return ret; } if (ret > 0) @@ -1209,8 +1200,7 @@ static int copy_space_cache(struct btrfs_root *root, num_bytes = btrfs_file_extent_disk_num_bytes(leaf, fi); ret = add_extent(bytenr, num_bytes, metadump, 1); if (ret) { - fprintf(stderr, "Error adding space cache blocks %d\n", - ret); + error("unable to add space cache blocks %d", ret); btrfs_release_path(path); return ret; } @@ -1239,7 +1229,7 @@ static int copy_from_extent_tree(struct metadump_struct *metadump, ret = btrfs_search_slot(NULL, extent_root, &key, path, 0, 0); if (ret < 0) { - fprintf(stderr, "Error searching extent root %d\n", ret); + error("extent root not found: %d", ret); return ret; } ret = 0; @@ -1250,8 +1240,7 @@ static int copy_from_extent_tree(struct metadump_struct *metadump, if (path->slots[0] >= btrfs_header_nritems(leaf)) { ret = btrfs_next_leaf(extent_root, path); if (ret < 0) { - fprintf(stderr, "Error going to next leaf %d" - "\n", ret); + error("cannot go to next leaf %d", ret); break; } if (ret > 0) { @@ -1270,10 +1259,18 @@ static int copy_from_extent_tree(struct metadump_struct *metadump, } bytenr = key.objectid; - if (key.type == BTRFS_METADATA_ITEM_KEY) + if (key.type == BTRFS_METADATA_ITEM_KEY) { num_bytes = extent_root->nodesize; - else + } else { num_bytes = key.offset; + } + + if (num_bytes == 0) { + error("extent length 0 at bytenr %llu key type %d", + (unsigned long long)bytenr, key.type); + ret = -EIO; + break; + } if (btrfs_item_size_nr(leaf, path->slots[0]) > sizeof(*ei)) { ei = btrfs_item_ptr(leaf, path->slots[0], @@ -1283,8 +1280,8 @@ static int copy_from_extent_tree(struct metadump_struct *metadump, ret = add_extent(bytenr, num_bytes, metadump, 0); if (ret) { - fprintf(stderr, "Error adding block " - "%d\n", ret); + error("unable to add block %llu: %d", + (unsigned long long)bytenr, ret); break; } } @@ -1292,8 +1289,8 @@ static int copy_from_extent_tree(struct metadump_struct *metadump, #ifdef BTRFS_COMPAT_EXTENT_TREE_V0 ret = is_tree_block(extent_root, path, bytenr); if (ret < 0) { - fprintf(stderr, "Error checking tree block " - "%d\n", ret); + error("failed to check tree block %llu: %d", + (unsigned long long)bytenr, ret); break; } @@ -1301,15 +1298,15 @@ static int copy_from_extent_tree(struct metadump_struct *metadump, ret = add_extent(bytenr, num_bytes, metadump, 0); if (ret) { - fprintf(stderr, "Error adding block " - "%d\n", ret); + error("unable to add block %llu: %d", + (unsigned long long)bytenr, ret); break; } } ret = 0; #else - fprintf(stderr, "Either extent tree corruption or " - "you haven't built with V0 support\n"); + error( + "either extent tree is corrupted or you haven't built with V0 support"); ret = -EIO; break; #endif @@ -1326,21 +1323,21 @@ static int create_metadump(const char *input, FILE *out, int num_threads, int compress_level, int sanitize, int walk_trees) { struct btrfs_root *root; - struct btrfs_path *path = NULL; + struct btrfs_path path; struct metadump_struct metadump; int ret; int err = 0; root = open_ctree(input, 0, 0); if (!root) { - fprintf(stderr, "Open ctree failed\n"); + error("open ctree failed"); return -EIO; } ret = metadump_init(&metadump, root, out, num_threads, compress_level, sanitize); if (ret) { - fprintf(stderr, "Error initializing metadump %d\n", ret); + error("failed to initialize metadump: %d", ret); close_ctree(root); return ret; } @@ -1348,17 +1345,12 @@ static int create_metadump(const char *input, FILE *out, int num_threads, ret = add_extent(BTRFS_SUPER_INFO_OFFSET, BTRFS_SUPER_INFO_SIZE, &metadump, 0); if (ret) { - fprintf(stderr, "Error adding metadata %d\n", ret); + error("unable to add metadata: %d", ret); err = ret; goto out; } - path = btrfs_alloc_path(); - if (!path) { - fprintf(stderr, "Out of memory allocating path\n"); - err = -ENOMEM; - goto out; - } + btrfs_init_path(&path); if (walk_trees) { ret = copy_tree_blocks(root, root->fs_info->chunk_root->node, @@ -1375,31 +1367,31 @@ static int create_metadump(const char *input, FILE *out, int num_threads, goto out; } } else { - ret = copy_from_extent_tree(&metadump, path); + ret = copy_from_extent_tree(&metadump, &path); if (ret) { err = ret; goto out; } } - ret = copy_log_trees(root, &metadump, path); + ret = copy_log_trees(root, &metadump, &path); if (ret) { err = ret; goto out; } - ret = copy_space_cache(root, &metadump, path); + ret = copy_space_cache(root, &metadump, &path); out: ret = flush_pending(&metadump, 1); if (ret) { if (!err) err = ret; - fprintf(stderr, "Error flushing pending %d\n", ret); + error("failed to flush pending data: %d", ret); } metadump_destroy(&metadump, num_threads); - btrfs_free_path(path); + btrfs_release_path(&path); ret = close_ctree(root); return err ? err : ret; } @@ -1494,8 +1486,7 @@ static int update_super(struct mdrestore_struct *mdres, u8 *buffer) BTRFS_UUID_SIZE); new_array_size += sizeof(*chunk); } else { - fprintf(stderr, "Bogus key in the sys chunk array " - "%d\n", key.type); + error("bogus key in the sys array %d", key.type); return -EIO; } write_ptr += sizeof(*chunk); @@ -1659,8 +1650,9 @@ static void write_backup_supers(int fd, u8 *buf) int ret; if (fstat(fd, &st)) { - fprintf(stderr, "Couldn't stat restore point, won't be able " - "to write backup supers: %d\n", errno); + error( + "cannot stat restore point, won't be able to write backup supers: %s", + strerror(errno)); return; } @@ -1675,11 +1667,11 @@ static void write_backup_supers(int fd, u8 *buf) ret = pwrite64(fd, buf, BTRFS_SUPER_INFO_SIZE, bytenr); if (ret < BTRFS_SUPER_INFO_SIZE) { if (ret < 0) - fprintf(stderr, "Problem writing out backup " - "super block %d, err %d\n", i, errno); + error( + "problem writing out backup super block %d: %s", + i, strerror(errno)); else - fprintf(stderr, "Short write writing out " - "backup super block\n"); + error("short write writing out backup super block"); break; } } @@ -1699,7 +1691,7 @@ static void *restore_worker(void *data) outfd = fileno(mdres->out); buffer = malloc(compress_size); if (!buffer) { - fprintf(stderr, "Error allocating buffer\n"); + error("not enough memory for restore worker buffer"); pthread_mutex_lock(&mdres->mutex); if (!mdres->error) mdres->error = -ENOMEM; @@ -1729,8 +1721,7 @@ static void *restore_worker(void *data) ret = uncompress(buffer, (unsigned long *)&size, async->buffer, async->bufsize); if (ret != Z_OK) { - fprintf(stderr, "Error decompressing %d\n", - ret); + error("decompressiion failed with %d", ret); err = -EIO; } outbuf = buffer; @@ -1785,18 +1776,18 @@ static void *restore_worker(void *data) error: if (ret < 0) { - fprintf(stderr, "Error writing to device %d\n", - errno); + error("unable to write to device: %s", + strerror(errno)); err = errno; } else { - fprintf(stderr, "Short write\n"); + error("short write"); err = -EIO; } } } else if (async->start != BTRFS_SUPER_INFO_OFFSET) { ret = write_data_to_disk(mdres->info, outbuf, async->start, size, 0); if (ret) { - printk("Error write data\n"); + error("failed to write data"); exit(1); } } @@ -1843,7 +1834,6 @@ static void mdrestore_destroy(struct mdrestore_struct *mdres, int num_threads) pthread_cond_destroy(&mdres->cond); pthread_mutex_destroy(&mdres->mutex); - free(mdres->threads); } static int mdrestore_init(struct mdrestore_struct *mdres, @@ -1873,14 +1863,14 @@ static int mdrestore_init(struct mdrestore_struct *mdres, return 0; mdres->num_threads = num_threads; - mdres->threads = calloc(num_threads, sizeof(pthread_t)); - if (!mdres->threads) - return -ENOMEM; for (i = 0; i < num_threads; i++) { - ret = pthread_create(mdres->threads + i, NULL, restore_worker, + ret = pthread_create(&mdres->threads[i], NULL, restore_worker, mdres); - if (ret) + if (ret) { + /* pthread_create returns errno directly */ + ret = -ret; break; + } } if (ret) mdrestore_destroy(mdres, i + 1); @@ -1908,7 +1898,7 @@ static int fill_mdres_info(struct mdrestore_struct *mdres, ret = uncompress(buffer, (unsigned long *)&size, async->buffer, async->bufsize); if (ret != Z_OK) { - fprintf(stderr, "Error decompressing %d\n", ret); + error("decompressiion failed with %d", ret); free(buffer); return -EIO; } @@ -1945,20 +1935,20 @@ static int add_cluster(struct meta_cluster *cluster, item = &cluster->items[i]; async = calloc(1, sizeof(*async)); if (!async) { - fprintf(stderr, "Error allocating async\n"); + error("not enough memory for async data"); return -ENOMEM; } async->start = le64_to_cpu(item->bytenr); async->bufsize = le32_to_cpu(item->size); async->buffer = malloc(async->bufsize); if (!async->buffer) { - fprintf(stderr, "Error allocating async buffer\n"); + error("not enough memory for async buffer"); free(async); return -ENOMEM; } ret = fread(async->buffer, async->bufsize, 1, mdres->in); if (ret != 1) { - fprintf(stderr, "Error reading buffer %d\n", errno); + error("unable to read buffer: %s", strerror(errno)); free(async->buffer); free(async); return -EIO; @@ -1969,7 +1959,7 @@ static int add_cluster(struct meta_cluster *cluster, if (async->start == BTRFS_SUPER_INFO_OFFSET) { ret = fill_mdres_info(mdres, async); if (ret) { - fprintf(stderr, "Error setting up restore\n"); + error("unable to set up restore state"); pthread_mutex_unlock(&mdres->mutex); free(async->buffer); free(async); @@ -1988,7 +1978,7 @@ static int add_cluster(struct meta_cluster *cluster, bytenr += size; ret = fread(buffer, size, 1, mdres->in); if (ret != 1) { - fprintf(stderr, "Error reading in buffer %d\n", errno); + error("failed to read buffer: %s", strerror(errno)); return -EIO; } } @@ -2037,20 +2027,25 @@ static int read_chunk_block(struct mdrestore_struct *mdres, u8 *buffer, memcpy(eb->data, buffer, mdres->nodesize); if (btrfs_header_bytenr(eb) != bytenr) { - fprintf(stderr, "Eb bytenr doesn't match found bytenr\n"); + error("eb bytenr does not match found bytenr: %llu != %llu", + (unsigned long long)btrfs_header_bytenr(eb), + (unsigned long long)bytenr); ret = -EIO; goto out; } if (memcmp(mdres->fsid, eb->data + offsetof(struct btrfs_header, fsid), BTRFS_FSID_SIZE)) { - fprintf(stderr, "Fsid doesn't match\n"); + error("filesystem UUID of eb %llu does not match", + (unsigned long long)bytenr); ret = -EIO; goto out; } if (btrfs_header_owner(eb) != BTRFS_CHUNK_TREE_OBJECTID) { - fprintf(stderr, "Does not belong to the chunk tree\n"); + error("wrong eb %llu owner %llu", + (unsigned long long)bytenr, + (unsigned long long)btrfs_header_owner(eb)); ret = -EIO; goto out; } @@ -2078,7 +2073,7 @@ static int read_chunk_block(struct mdrestore_struct *mdres, u8 *buffer, fs_chunk = malloc(sizeof(struct fs_chunk)); if (!fs_chunk) { - fprintf(stderr, "Error allocating chunk\n"); + error("not enough memory to allocate chunk"); ret = -ENOMEM; break; } @@ -2137,13 +2132,13 @@ static int search_for_chunk_blocks(struct mdrestore_struct *mdres, cluster = malloc(BLOCK_SIZE); if (!cluster) { - fprintf(stderr, "Error allocating cluster\n"); + error("not enough memory for cluster"); return -ENOMEM; } buffer = malloc(max_size); if (!buffer) { - fprintf(stderr, "Error allocating buffer\n"); + error("not enough memory for buffer"); free(cluster); return -ENOMEM; } @@ -2151,7 +2146,7 @@ static int search_for_chunk_blocks(struct mdrestore_struct *mdres, if (mdres->compress_method == COMPRESS_ZLIB) { tmp = malloc(max_size); if (!tmp) { - fprintf(stderr, "Error allocating tmp buffer\n"); + error("not enough memory for buffer"); free(cluster); free(buffer); return -ENOMEM; @@ -2161,7 +2156,7 @@ static int search_for_chunk_blocks(struct mdrestore_struct *mdres, bytenr = current_cluster; while (1) { if (fseek(mdres->in, current_cluster, SEEK_SET)) { - fprintf(stderr, "Error seeking: %d\n", errno); + error("seek failed: %s", strerror(errno)); ret = -EIO; break; } @@ -2174,11 +2169,15 @@ static int search_for_chunk_blocks(struct mdrestore_struct *mdres, bytenr = 0; continue; } - printf("ok this is where we screwed up?\n"); + error( + "unknown state after reading cluster at %llu, probably crrupted data", + cluster_bytenr); ret = -EIO; break; } else if (ret < 0) { - fprintf(stderr, "Error reading image\n"); + error("unable to read image at %llu: %s", + (unsigned long long)cluster_bytenr, + strerror(errno)); break; } ret = 0; @@ -2186,7 +2185,7 @@ static int search_for_chunk_blocks(struct mdrestore_struct *mdres, header = &cluster->header; if (le64_to_cpu(header->magic) != HEADER_MAGIC || le64_to_cpu(header->bytenr) != current_cluster) { - fprintf(stderr, "bad header in metadump image\n"); + error("bad header in metadump image"); ret = -EIO; break; } @@ -2201,8 +2200,8 @@ static int search_for_chunk_blocks(struct mdrestore_struct *mdres, item_bytenr = le64_to_cpu(item->bytenr); if (bufsize > max_size) { - fprintf(stderr, "item %u size %u too big\n", - i, bufsize); + error("item %u too big: %u > %u", i, bufsize, + max_size); ret = -EIO; break; } @@ -2210,8 +2209,7 @@ static int search_for_chunk_blocks(struct mdrestore_struct *mdres, if (mdres->compress_method == COMPRESS_ZLIB) { ret = fread(tmp, bufsize, 1, mdres->in); if (ret != 1) { - fprintf(stderr, "Error reading: %d\n", - errno); + error("read error: %s", strerror(errno)); ret = -EIO; break; } @@ -2221,16 +2219,16 @@ static int search_for_chunk_blocks(struct mdrestore_struct *mdres, (unsigned long *)&size, tmp, bufsize); if (ret != Z_OK) { - fprintf(stderr, "Error decompressing " - "%d\n", ret); + error("decompressiion failed with %d", + ret); ret = -EIO; break; } } else { ret = fread(buffer, bufsize, 1, mdres->in); if (ret != 1) { - fprintf(stderr, "Error reading: %d\n", - errno); + error("read error: %s", + strerror(errno)); ret = -EIO; break; } @@ -2283,7 +2281,7 @@ static int build_chunk_tree(struct mdrestore_struct *mdres, ret = fread(cluster, BLOCK_SIZE, 1, mdres->in); if (ret <= 0) { - fprintf(stderr, "Error reading in cluster: %d\n", errno); + error("unable to read cluster: %s", strerror(errno)); return -EIO; } ret = 0; @@ -2291,7 +2289,7 @@ static int build_chunk_tree(struct mdrestore_struct *mdres, header = &cluster->header; if (le64_to_cpu(header->magic) != HEADER_MAGIC || le64_to_cpu(header->bytenr) != 0) { - fprintf(stderr, "bad header in metadump image\n"); + error("bad header in metadump image"); return -EIO; } @@ -2305,25 +2303,26 @@ static int build_chunk_tree(struct mdrestore_struct *mdres, break; bytenr += le32_to_cpu(item->size); if (fseek(mdres->in, le32_to_cpu(item->size), SEEK_CUR)) { - fprintf(stderr, "Error seeking: %d\n", errno); + error("seek failed: %s", strerror(errno)); return -EIO; } } if (!item || le64_to_cpu(item->bytenr) != BTRFS_SUPER_INFO_OFFSET) { - fprintf(stderr, "Huh, didn't find the super?\n"); + error("did not find superblock at %llu", + le64_to_cpu(item->bytenr)); return -EINVAL; } buffer = malloc(le32_to_cpu(item->size)); if (!buffer) { - fprintf(stderr, "Error allocating buffer\n"); + error("not enough memory to allocate buffer"); return -ENOMEM; } ret = fread(buffer, le32_to_cpu(item->size), 1, mdres->in); if (ret != 1) { - fprintf(stderr, "Error reading buffer: %d\n", errno); + error("unable to read buffer: %s", strerror(errno)); free(buffer); return -EIO; } @@ -2340,7 +2339,7 @@ static int build_chunk_tree(struct mdrestore_struct *mdres, ret = uncompress(tmp, (unsigned long *)&size, buffer, le32_to_cpu(item->size)); if (ret != Z_OK) { - fprintf(stderr, "Error decompressing %d\n", ret); + error("decompressiion failed with %d", ret); free(buffer); free(tmp); return -EIO; @@ -2388,9 +2387,8 @@ static void remap_overlapping_chunks(struct mdrestore_struct *mdres) list_del_init(&fs_chunk->list); if (range_contains_super(fs_chunk->physical, fs_chunk->bytes)) { - fprintf(stderr, "Remapping a chunk that had a super " - "mirror inside of it, clearing space cache " - "so we don't end up with corruption\n"); + warning( +"remapping a chunk that had a super mirror inside of it, clearing space cache so we don't end up with corruption"); mdres->clear_space_cache = 1; } fs_chunk->physical = mdres->last_physical_offset; @@ -2404,24 +2402,16 @@ static int fixup_devices(struct btrfs_fs_info *fs_info, { struct btrfs_trans_handle *trans; struct btrfs_dev_item *dev_item; - struct btrfs_path *path; + struct btrfs_path path; struct extent_buffer *leaf; struct btrfs_root *root = fs_info->chunk_root; struct btrfs_key key; u64 devid, cur_devid; int ret; - path = btrfs_alloc_path(); - if (!path) { - fprintf(stderr, "Error allocating path\n"); - return -ENOMEM; - } - trans = btrfs_start_transaction(fs_info->tree_root, 1); if (IS_ERR(trans)) { - fprintf(stderr, "Error starting transaction %ld\n", - PTR_ERR(trans)); - btrfs_free_path(path); + error("cannot starting transaction %ld", PTR_ERR(trans)); return PTR_ERR(trans); } @@ -2436,48 +2426,48 @@ static int fixup_devices(struct btrfs_fs_info *fs_info, key.type = BTRFS_DEV_ITEM_KEY; key.offset = 0; + btrfs_init_path(&path); + again: - ret = btrfs_search_slot(trans, root, &key, path, -1, 1); + ret = btrfs_search_slot(trans, root, &key, &path, -1, 1); if (ret < 0) { - fprintf(stderr, "search failed %d\n", ret); + error("search failed: %d", ret); exit(1); } while (1) { - leaf = path->nodes[0]; - if (path->slots[0] >= btrfs_header_nritems(leaf)) { - ret = btrfs_next_leaf(root, path); + leaf = path.nodes[0]; + if (path.slots[0] >= btrfs_header_nritems(leaf)) { + ret = btrfs_next_leaf(root, &path); if (ret < 0) { - fprintf(stderr, "Error going to next leaf " - "%d\n", ret); + error("cannot go to next leaf %d", ret); exit(1); } if (ret > 0) { ret = 0; break; } - leaf = path->nodes[0]; + leaf = path.nodes[0]; } - btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); + btrfs_item_key_to_cpu(leaf, &key, path.slots[0]); if (key.type > BTRFS_DEV_ITEM_KEY) break; if (key.type != BTRFS_DEV_ITEM_KEY) { - path->slots[0]++; + path.slots[0]++; continue; } - dev_item = btrfs_item_ptr(leaf, path->slots[0], + dev_item = btrfs_item_ptr(leaf, path.slots[0], struct btrfs_dev_item); cur_devid = btrfs_device_id(leaf, dev_item); if (devid != cur_devid) { - ret = btrfs_del_item(trans, root, path); + ret = btrfs_del_item(trans, root, &path); if (ret) { - fprintf(stderr, "Error deleting item %d\n", - ret); + error("cannot delete item: %d", ret); exit(1); } - btrfs_release_path(path); + btrfs_release_path(&path); goto again; } @@ -2485,13 +2475,13 @@ again: btrfs_set_device_bytes_used(leaf, dev_item, mdres->alloced_chunks); btrfs_mark_buffer_dirty(leaf); - path->slots[0]++; + path.slots[0]++; } - btrfs_free_path(path); + btrfs_release_path(&path); ret = btrfs_commit_transaction(trans, fs_info->tree_root); if (ret) { - fprintf(stderr, "Commit failed %d\n", ret); + error("unable to commit transaction: %d", ret); return ret; } return 0; @@ -2514,20 +2504,20 @@ static int restore_metadump(const char *input, FILE *out, int old_restore, } else { in = fopen(input, "r"); if (!in) { - perror("unable to open metadump image"); + error("unable to open metadump image: %s", + strerror(errno)); return 1; } } /* NOTE: open with write mode */ if (fixup_offset) { - BUG_ON(!target); info = open_ctree_fs_info(target, 0, 0, 0, OPEN_CTREE_WRITES | OPEN_CTREE_RESTORE | OPEN_CTREE_PARTIAL); if (!info) { - fprintf(stderr, "%s: open ctree failed\n", __func__); + error("open ctree failed"); ret = -EIO; goto failed_open; } @@ -2535,7 +2525,7 @@ static int restore_metadump(const char *input, FILE *out, int old_restore, cluster = malloc(BLOCK_SIZE); if (!cluster) { - fprintf(stderr, "Error allocating cluster\n"); + error("not enough memory for cluster"); ret = -ENOMEM; goto failed_info; } @@ -2543,7 +2533,7 @@ static int restore_metadump(const char *input, FILE *out, int old_restore, ret = mdrestore_init(&mdrestore, in, out, old_restore, num_threads, fixup_offset, info, multi_devices); if (ret) { - fprintf(stderr, "Error initializing mdrestore %d\n", ret); + error("failed to initialize metadata restore state: %d", ret); goto failed_cluster; } @@ -2556,7 +2546,7 @@ static int restore_metadump(const char *input, FILE *out, int old_restore, } if (in != stdin && fseek(in, 0, SEEK_SET)) { - fprintf(stderr, "Error seeking %d\n", errno); + error("seek failed: %s", strerror(errno)); goto out; } @@ -2568,13 +2558,13 @@ static int restore_metadump(const char *input, FILE *out, int old_restore, header = &cluster->header; if (le64_to_cpu(header->magic) != HEADER_MAGIC || le64_to_cpu(header->bytenr) != bytenr) { - fprintf(stderr, "bad header in metadump image\n"); + error("bad header in metadump image"); ret = -EIO; break; } ret = add_cluster(cluster, &mdrestore, &bytenr); if (ret) { - fprintf(stderr, "Error adding cluster\n"); + error("failed to add cluster: %d", ret); break; } } @@ -2589,14 +2579,14 @@ static int restore_metadump(const char *input, FILE *out, int old_restore, OPEN_CTREE_WRITES | OPEN_CTREE_NO_DEVICES); if (!root) { - fprintf(stderr, "unable to open %s\n", target); + error("open ctree failed in %s", target); ret = -EIO; goto out; } info = root->fs_info; if (stat(target, &st)) { - fprintf(stderr, "statting %s failed\n", target); + error("stat %s failed: %s", target, strerror(errno)); close_ctree(info->chunk_root); free(cluster); return 1; @@ -2643,7 +2633,7 @@ static int update_disk_super_on_device(struct btrfs_fs_info *info, btrfs_init_path(&path); ret = btrfs_search_slot(NULL, info->chunk_root, &key, &path, 0, 0); if (ret) { - fprintf(stderr, "ERROR: search key failed\n"); + error("search key failed: %d", ret); ret = -EIO; goto out; } @@ -2654,7 +2644,9 @@ static int update_disk_super_on_device(struct btrfs_fs_info *info, devid = btrfs_device_id(leaf, dev_item); if (devid != cur_devid) { - printk("ERROR: devid %llu mismatch with %llu\n", devid, cur_devid); + error("devid mismatch: %llu != %llu", + (unsigned long long)devid, + (unsigned long long)cur_devid); ret = -EIO; goto out; } @@ -2670,12 +2662,12 @@ static int update_disk_super_on_device(struct btrfs_fs_info *info, btrfs_release_path(&path); - printk("update disk super on %s devid=%llu\n", other_dev, devid); + printf("update disk super on %s devid=%llu\n", other_dev, devid); /* update other devices' super block */ fp = open(other_dev, O_CREAT | O_RDWR, 0600); if (fp < 0) { - fprintf(stderr, "ERROR: could not open %s\n", other_dev); + error("could not open %s: %s", other_dev, strerror(errno)); ret = -EIO; goto out; } @@ -2699,9 +2691,9 @@ static int update_disk_super_on_device(struct btrfs_fs_info *info, ret = pwrite64(fp, buf, BTRFS_SUPER_INFO_SIZE, BTRFS_SUPER_INFO_OFFSET); if (ret != BTRFS_SUPER_INFO_SIZE) { if (ret < 0) - fprintf(stderr, "ERROR: cannot write superblock: %s\n", strerror(ret)); + error("cannot write superblock: %s", strerror(ret)); else - fprintf(stderr, "ERROR: cannot write superblock\n"); + error("cannot write superblock"); ret = -EIO; goto out; } @@ -2760,9 +2752,10 @@ int main(int argc, char *argv[]) break; case 't': num_threads = arg_strtou64(optarg); - if (num_threads > 32) { - error("number of threads out of range: %llu", - (unsigned long long)num_threads); + if (num_threads > MAX_WORKER_THREADS) { + error("number of threads out of range: %llu > %d", + (unsigned long long)num_threads, + MAX_WORKER_THREADS); return 1; } break; @@ -2801,20 +2794,22 @@ int main(int argc, char *argv[]) if (create) { if (old_restore) { - fprintf(stderr, "Usage error: create and restore cannot be used at the same time\n"); + error( + "create and restore cannot be used at the same time"); usage_error++; } } else { if (walk_trees || sanitize || compress_level) { - fprintf(stderr, "Usage error: use -w, -s, -c options for restore makes no sense\n"); + error( + "useing -w, -s, -c options for restore makes no sense"); usage_error++; } if (multi_devices && dev_cnt < 2) { - fprintf(stderr, "Usage error: not enough devices specified for -m option\n"); + error("not enough devices specified for -m option"); usage_error++; } if (!multi_devices && dev_cnt != 1) { - fprintf(stderr, "Usage error: accepts only 1 device without -m option\n"); + error("accepts only 1 device without -m option"); usage_error++; } } @@ -2830,7 +2825,7 @@ int main(int argc, char *argv[]) } else { out = fopen(target, "w+"); if (!out) { - perror("unable to create target file"); + error("unable to create target file %s", target); exit(1); } } @@ -2850,12 +2845,12 @@ int main(int argc, char *argv[]) if (create) { ret = check_mounted(source); if (ret < 0) { - fprintf(stderr, "Could not check mount status: %s\n", - strerror(-ret)); - exit(1); - } else if (ret) - fprintf(stderr, - "WARNING: The device is mounted. Make sure the filesystem is quiescent.\n"); + warning("unable to check mount status of: %s", + strerror(-ret)); + } else if (ret) { + warning("%s already mounted, results may be inaccurate", + source); + } ret = create_metadump(source, out, num_threads, compress_level, sanitize, walk_trees); @@ -2864,7 +2859,7 @@ int main(int argc, char *argv[]) 0, target, multi_devices); } if (ret) { - printk("%s failed (%s)\n", (create) ? "create" : "restore", + error("%s failed: %s", (create) ? "create" : "restore", strerror(errno)); goto out; } @@ -2879,14 +2874,13 @@ int main(int argc, char *argv[]) OPEN_CTREE_PARTIAL | OPEN_CTREE_RESTORE); if (!info) { - fprintf(stderr, "unable to open %s error = %s\n", - target, strerror(errno)); + error("open ctree failed at %s", target); return 1; } total_devs = btrfs_super_num_devices(info->super_copy); if (total_devs != dev_cnt) { - printk("it needs %llu devices but has only %d\n", + error("it needs %llu devices but has only %d", total_devs, dev_cnt); close_ctree(info->chunk_root); goto out; @@ -2897,7 +2891,7 @@ int main(int argc, char *argv[]) ret = update_disk_super_on_device(info, argv[optind + i], (u64)i); if (ret) { - printk("update disk super failed devid=%d (error=%d)\n", + error("update disk superblock failed devid %d: %d", i, ret); close_ctree(info->chunk_root); exit(1); @@ -2910,8 +2904,7 @@ int main(int argc, char *argv[]) ret = restore_metadump(source, out, 0, num_threads, 1, target, 1); if (ret) { - fprintf(stderr, "fix metadump failed (error=%d)\n", - ret); + error("unable to fixup metadump: %d", ret); exit(1); } } @@ -2925,9 +2918,8 @@ out: unlink_ret = unlink(target); if (unlink_ret) - fprintf(stderr, - "unlink output file failed : %s\n", - strerror(errno)); + error("unlink output file %s failed: %s", + target, strerror(errno)); } } diff --git a/inode-item.c b/inode-item.c index 522d25a4..5dd79dd3 100644 --- a/inode-item.c +++ b/inode-item.c @@ -19,7 +19,7 @@ #include "ctree.h" #include "disk-io.h" #include "transaction.h" -#include "crc32c.h" +#include "hash.h" static int find_name_in_backref(struct btrfs_path *path, const char * name, int name_len, struct btrfs_inode_ref **ref_ret) @@ -64,7 +64,7 @@ int btrfs_insert_inode_ref(struct btrfs_trans_handle *trans, key.objectid = inode_objectid; key.offset = ref_objectid; - btrfs_set_key_type(&key, BTRFS_INODE_REF_KEY); + key.type = BTRFS_INODE_REF_KEY; path = btrfs_alloc_path(); if (!path) @@ -106,8 +106,7 @@ out: btrfs_free_path(path); if (ret == -EMLINK) { - if (btrfs_fs_incompat(root->fs_info, - BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF)) + if (btrfs_fs_incompat(root->fs_info, EXTENDED_IREF)) ret = btrfs_insert_inode_extref(trans, root, name, name_len, inode_objectid, @@ -128,13 +127,13 @@ int btrfs_lookup_inode(struct btrfs_trans_handle *trans, struct btrfs_root struct btrfs_key found_key; ret = btrfs_search_slot(trans, root, location, path, ins_len, cow); - if (ret > 0 && btrfs_key_type(location) == BTRFS_ROOT_ITEM_KEY && + if (ret > 0 && location->type == BTRFS_ROOT_ITEM_KEY && location->offset == (u64)-1 && path->slots[0] != 0) { slot = path->slots[0] - 1; leaf = path->nodes[0]; btrfs_item_key_to_cpu(leaf, &found_key, slot); if (found_key.objectid == location->objectid && - btrfs_key_type(&found_key) == btrfs_key_type(location)) { + found_key.type == location->type) { path->slots[0]--; return 0; } @@ -184,12 +183,6 @@ out: return ret_inode_ref; } -static inline u64 btrfs_extref_hash(u64 parent_ino, const char *name, - int namelen) -{ - return (u64)btrfs_crc32c(parent_ino, name, namelen); -} - static int btrfs_find_name_in_ext_backref(struct btrfs_path *path, u64 parent_ino, const char *name, int namelen, struct btrfs_inode_extref **extref_ret) @@ -446,8 +439,7 @@ out: btrfs_free_path(path); if (search_ext_refs && - btrfs_fs_incompat(root->fs_info, - BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF)) { + btrfs_fs_incompat(root->fs_info, EXTENDED_IREF)) { /* * No refs were found, or we could not find the name in our ref * array. Find and remove the extended inode ref then. diff --git a/inode-map.c b/inode-map.c index 9e4dcd3c..9000e69b 100644 --- a/inode-map.c +++ b/inode-map.c @@ -39,7 +39,9 @@ int btrfs_find_free_objectid(struct btrfs_trans_handle *trans, u64 search_start = dirid; path = btrfs_alloc_path(); - BUG_ON(!path); + if (!path) + return -ENOMEM; + search_start = root->last_inode_alloc; search_start = max((unsigned long long)search_start, BTRFS_FIRST_FREE_OBJECTID); diff --git a/ioctl-test.c b/ioctl-test.c index 54fc0135..65d584be 100644 --- a/ioctl-test.c +++ b/ioctl-test.c @@ -1,37 +1,252 @@ +/* + * 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 "kerncompat.h" + #include "ioctl.h" +#include "ctree.h" + +#define LIST_32_COMPAT \ + ONE(BTRFS_IOC_SET_RECEIVED_SUBVOL_32) + +#define LIST_64_COMPAT \ + ONE(BTRFS_IOC_SEND_64) + +#define LIST_BASE \ + ONE(BTRFS_IOC_SNAP_CREATE) \ + ONE(BTRFS_IOC_DEFRAG) \ + ONE(BTRFS_IOC_RESIZE) \ + ONE(BTRFS_IOC_SCAN_DEV) \ + ONE(BTRFS_IOC_TRANS_START) \ + ONE(BTRFS_IOC_TRANS_END) \ + ONE(BTRFS_IOC_SYNC) \ + ONE(BTRFS_IOC_CLONE) \ + ONE(BTRFS_IOC_ADD_DEV) \ + ONE(BTRFS_IOC_RM_DEV) \ + ONE(BTRFS_IOC_BALANCE) \ + ONE(BTRFS_IOC_CLONE_RANGE) \ + ONE(BTRFS_IOC_SUBVOL_CREATE) \ + ONE(BTRFS_IOC_SNAP_DESTROY) \ + ONE(BTRFS_IOC_DEFRAG_RANGE) \ + ONE(BTRFS_IOC_TREE_SEARCH) \ + ONE(BTRFS_IOC_TREE_SEARCH_V2) \ + ONE(BTRFS_IOC_INO_LOOKUP) \ + ONE(BTRFS_IOC_DEFAULT_SUBVOL) \ + ONE(BTRFS_IOC_SPACE_INFO) \ + ONE(BTRFS_IOC_START_SYNC) \ + ONE(BTRFS_IOC_WAIT_SYNC) \ + ONE(BTRFS_IOC_SNAP_CREATE_V2) \ + ONE(BTRFS_IOC_SUBVOL_CREATE_V2) \ + ONE(BTRFS_IOC_SUBVOL_GETFLAGS) \ + ONE(BTRFS_IOC_SUBVOL_SETFLAGS) \ + ONE(BTRFS_IOC_SCRUB) \ + ONE(BTRFS_IOC_SCRUB_CANCEL) \ + ONE(BTRFS_IOC_SCRUB_PROGRESS) \ + ONE(BTRFS_IOC_DEV_INFO) \ + ONE(BTRFS_IOC_FS_INFO) \ + ONE(BTRFS_IOC_BALANCE_V2) \ + ONE(BTRFS_IOC_BALANCE_CTL) \ + ONE(BTRFS_IOC_BALANCE_PROGRESS) \ + ONE(BTRFS_IOC_INO_PATHS) \ + ONE(BTRFS_IOC_LOGICAL_INO) \ + ONE(BTRFS_IOC_SET_RECEIVED_SUBVOL) \ + ONE(BTRFS_IOC_SEND) \ + ONE(BTRFS_IOC_DEVICES_READY) \ + ONE(BTRFS_IOC_QUOTA_CTL) \ + ONE(BTRFS_IOC_QGROUP_ASSIGN) \ + ONE(BTRFS_IOC_QGROUP_CREATE) \ + ONE(BTRFS_IOC_QGROUP_LIMIT) \ + ONE(BTRFS_IOC_QUOTA_RESCAN) \ + ONE(BTRFS_IOC_QUOTA_RESCAN_STATUS) \ + ONE(BTRFS_IOC_QUOTA_RESCAN_WAIT) \ + ONE(BTRFS_IOC_GET_FSLABEL) \ + ONE(BTRFS_IOC_SET_FSLABEL) \ + ONE(BTRFS_IOC_GET_DEV_STATS) \ + ONE(BTRFS_IOC_DEV_REPLACE) \ + ONE(BTRFS_IOC_FILE_EXTENT_SAME) \ + ONE(BTRFS_IOC_GET_FEATURES) \ + ONE(BTRFS_IOC_SET_FEATURES) \ + ONE(BTRFS_IOC_GET_SUPPORTED_FEATURES) \ + ONE(BTRFS_IOC_RM_DEV_V2) -static unsigned long ioctls[] = { - BTRFS_IOC_SNAP_CREATE, - BTRFS_IOC_DEFRAG, - BTRFS_IOC_RESIZE, - BTRFS_IOC_SCAN_DEV, - BTRFS_IOC_TRANS_START, - BTRFS_IOC_TRANS_END, - BTRFS_IOC_SYNC, - BTRFS_IOC_CLONE, - BTRFS_IOC_ADD_DEV, - BTRFS_IOC_RM_DEV, - BTRFS_IOC_BALANCE, - BTRFS_IOC_SUBVOL_CREATE, - BTRFS_IOC_SNAP_DESTROY, - BTRFS_IOC_DEFRAG_RANGE, - BTRFS_IOC_TREE_SEARCH, - BTRFS_IOC_INO_LOOKUP, - BTRFS_IOC_DEFAULT_SUBVOL, - BTRFS_IOC_SPACE_INFO, - BTRFS_IOC_SNAP_CREATE_V2, - 0 }; +#define LIST \ + LIST_BASE \ + LIST_32_COMPAT \ + LIST_64_COMPAT + +struct ioctl_number { + unsigned long defined; + unsigned long expected; +}; + +static struct ioctl_number expected_list[] = { + { BTRFS_IOC_SNAP_CREATE, 0x0050009401 }, + { BTRFS_IOC_DEFRAG, 0x0050009402 }, + { BTRFS_IOC_RESIZE, 0x0050009403 }, + { BTRFS_IOC_SCAN_DEV, 0x0050009404 }, + { BTRFS_IOC_TRANS_START, 0x0000009406 }, + { BTRFS_IOC_TRANS_END, 0x0000009407 }, + { BTRFS_IOC_SYNC, 0x0000009408 }, + { BTRFS_IOC_CLONE, 0x0040049409 }, + { BTRFS_IOC_ADD_DEV, 0x005000940a }, + { BTRFS_IOC_RM_DEV, 0x005000940b }, + { BTRFS_IOC_BALANCE, 0x005000940c }, + { BTRFS_IOC_CLONE_RANGE, 0x004020940d }, + { BTRFS_IOC_SUBVOL_CREATE, 0x005000940e }, + { BTRFS_IOC_SNAP_DESTROY, 0x005000940f }, + { BTRFS_IOC_DEFRAG_RANGE, 0x0040309410 }, + { BTRFS_IOC_TREE_SEARCH, 0x00d0009411 }, + { BTRFS_IOC_TREE_SEARCH_V2, 0x00c0709411 }, + { BTRFS_IOC_INO_LOOKUP, 0x00d0009412 }, + { BTRFS_IOC_DEFAULT_SUBVOL, 0x0040089413 }, + { BTRFS_IOC_SPACE_INFO, 0x00c0109414 }, + { BTRFS_IOC_START_SYNC, 0x0080089418 }, + { BTRFS_IOC_WAIT_SYNC, 0x0040089416 }, + { BTRFS_IOC_SNAP_CREATE_V2, 0x0050009417 }, + { BTRFS_IOC_SUBVOL_CREATE_V2, 0x0050009418 }, + { BTRFS_IOC_SUBVOL_GETFLAGS, 0x0080089419 }, + { BTRFS_IOC_SUBVOL_SETFLAGS, 0x004008941a }, + { BTRFS_IOC_SCRUB, 0x00c400941b }, + { BTRFS_IOC_SCRUB_CANCEL, 0x000000941c }, + { BTRFS_IOC_SCRUB_PROGRESS, 0x00c400941d }, + { BTRFS_IOC_DEV_INFO, 0x00d000941e }, + { BTRFS_IOC_FS_INFO, 0x008400941f }, + { BTRFS_IOC_BALANCE_V2, 0x00c4009420 }, + { BTRFS_IOC_BALANCE_CTL, 0x0040049421 }, + { BTRFS_IOC_BALANCE_PROGRESS, 0x0084009422 }, + { BTRFS_IOC_INO_PATHS, 0x00c0389423 }, + { BTRFS_IOC_LOGICAL_INO, 0x00c0389424 }, + { BTRFS_IOC_SET_RECEIVED_SUBVOL, 0x00c0c89425 }, +#ifdef BTRFS_IOC_SET_RECEIVED_SUBVOL_32_COMPAT_DEFINED + { BTRFS_IOC_SET_RECEIVED_SUBVOL_32, 0x00c0c09425 }, +#endif +#if BITS_PER_LONG == 32 + { BTRFS_IOC_SEND, 0x0040449426 }, +#elif BITS_PER_LONG == 64 + { BTRFS_IOC_SEND, 0x0040489426 }, +#endif +#ifdef BTRFS_IOC_SEND_64_COMPAT_DEFINED + { BTRFS_IOC_SEND_64, 0x0040489426 }, +#endif + { BTRFS_IOC_DEVICES_READY, 0x0090009427 }, + { BTRFS_IOC_QUOTA_CTL, 0x00c0109428 }, + { BTRFS_IOC_QGROUP_ASSIGN, 0x0040189429 }, + { BTRFS_IOC_QGROUP_CREATE, 0x004010942a }, + { BTRFS_IOC_QGROUP_LIMIT, 0x008030942b }, + { BTRFS_IOC_QUOTA_RESCAN, 0x004040942c }, + { BTRFS_IOC_QUOTA_RESCAN_STATUS, 0x008040942d }, + { BTRFS_IOC_QUOTA_RESCAN_WAIT, 0x000000942e }, + { BTRFS_IOC_GET_FSLABEL, 0x0081009431 }, + { BTRFS_IOC_SET_FSLABEL, 0x0041009432 }, + { BTRFS_IOC_GET_DEV_STATS, 0x00c4089434 }, + { BTRFS_IOC_DEV_REPLACE, 0x00ca289435 }, + { BTRFS_IOC_FILE_EXTENT_SAME, 0x00c0189436 }, + { BTRFS_IOC_GET_FEATURES, 0x0080189439 }, + { BTRFS_IOC_SET_FEATURES, 0x0040309439 }, + { BTRFS_IOC_GET_SUPPORTED_FEATURES, 0x0080489439 }, + { BTRFS_IOC_RM_DEV_V2, 0x005000943a }, +}; + +static struct btrfs_ioctl_vol_args used_vol_args __attribute__((used)); +static struct btrfs_ioctl_vol_args_v2 used_vol_args2 __attribute__((used)); +static struct btrfs_ioctl_clone_range_args used_clone_args __attribute__((used)); +static struct btrfs_ioctl_defrag_range_args used_defrag_args __attribute__((used)); +static struct btrfs_ioctl_search_args used_search_args __attribute__((used)); +static struct btrfs_ioctl_search_args_v2 used_search_args2 __attribute__((used)); +static struct btrfs_ioctl_ino_lookup_args used_ino_lookup __attribute__((used)); +static struct btrfs_ioctl_space_args used_space_args __attribute__((used)); +static struct btrfs_ioctl_scrub_args used_scrub_args __attribute__((used)); +static struct btrfs_ioctl_dev_info_args used_dev_info_args __attribute__((used)); +static struct btrfs_ioctl_fs_info_args used_fs_info_args __attribute__((used)); +static struct btrfs_ioctl_balance_args used_balance_args __attribute__((used)); +static struct btrfs_ioctl_ino_path_args used_path_args __attribute__((used)); +static struct btrfs_ioctl_logical_ino_args used_logical_args __attribute__((used)); +static struct btrfs_ioctl_received_subvol_args used_received_args __attribute__((used)); +#ifdef BTRFS_IOC_SET_RECEIVED_SUBVOL_32_COMPAT_DEFINED +static struct btrfs_ioctl_received_subvol_args_32 used_received_args32 __attribute__((used)); +#endif +static struct btrfs_ioctl_send_args used_send_args __attribute__((used)); +#ifdef BTRFS_IOC_SEND_64_COMPAT_DEFINED +static struct btrfs_ioctl_send_args_64 used_send_args64 __attribute__((used)); +#endif +static struct btrfs_ioctl_quota_ctl_args used_qgctl_args __attribute__((used)); +static struct btrfs_ioctl_qgroup_assign_args used_qgassign_args __attribute__((used)); +static struct btrfs_ioctl_qgroup_create_args used_qgcreate_args __attribute__((used)); +static struct btrfs_ioctl_qgroup_limit_args used_qglimit_args __attribute__((used)); +static struct btrfs_ioctl_quota_rescan_args used_qgrescan_args __attribute__((used)); +static struct btrfs_ioctl_get_dev_stats used_dev_stats_args __attribute__((used)); +static struct btrfs_ioctl_dev_replace_args used_replace_args __attribute__((used)); +static struct btrfs_ioctl_same_args used_same_args __attribute__((used)); +static struct btrfs_ioctl_feature_flags used_feature_flags __attribute__((used)); + +const char* value_to_string(unsigned long num) +{ +#define ONE(x) case x: return #x; + switch (num) { + LIST_BASE + } + + switch (num) { + LIST_32_COMPAT + } + + switch (num) { + LIST_64_COMPAT + } +#undef ONE + return "UNKNOWN"; +} int main(int ac, char **av) { - int i = 0; - while(ioctls[i]) { - printf("%lu\n" ,ioctls[i]); - i++; + int i; + int errors = 0; + + value_to_string(random()); + + printf("Sizeof long long: %zu\n", sizeof(unsigned long long)); + printf("Sizeof long: %zu\n", sizeof(unsigned long)); + printf("Sizeof pointer: %zu\n", sizeof(void*)); + printf("Alignof long long: %zu\n", __alignof__(unsigned long long)); + printf("Alignof long: %zu\n", __alignof__(unsigned long)); + printf("Alignof pointer: %zu\n", __alignof__(void*)); + printf("Raw ioctl numbers:\n"); + +#define ONE(n) printf("%-38s 0x%010lx\n", #n, (unsigned long)n); + LIST +#undef ONE + + for (i = 0; i < ARRAY_SIZE(expected_list); i++) { + if (expected_list[i].defined != expected_list[i].expected) { + printf("ERROR: wrong value for %s, defined=0x%lx expected=0x%lx\n", + value_to_string(expected_list[i].defined), + expected_list[i].defined, + expected_list[i].expected); + errors++; + } } - return 0; + + if (!errors) { + printf("All ok\n"); + } else { + printf("Found %d errors in definitions\n", errors); + } + + return !!errors; } @@ -25,11 +25,17 @@ extern "C" { #include <asm/types.h> #include <linux/ioctl.h> +#include <stddef.h> #ifndef __user #define __user #endif +/* We don't want to include entire kerncompat.h */ +#ifndef BUILD_ASSERT +#define BUILD_ASSERT(x) +#endif + #define BTRFS_IOCTL_MAGIC 0x94 #define BTRFS_VOL_NAME_MAX 255 @@ -39,6 +45,7 @@ struct btrfs_ioctl_vol_args { __s64 fd; char name[BTRFS_PATH_NAME_MAX + 1]; }; +BUILD_ASSERT(sizeof(struct btrfs_ioctl_vol_args) == 4096); #define BTRFS_DEVICE_PATH_NAME_MAX 1024 @@ -65,6 +72,7 @@ struct btrfs_qgroup_limit { __u64 rsv_referenced; __u64 rsv_exclusive; }; +BUILD_ASSERT(sizeof(struct btrfs_qgroup_limit) == 40); struct btrfs_qgroup_inherit { __u64 flags; @@ -74,11 +82,13 @@ struct btrfs_qgroup_inherit { struct btrfs_qgroup_limit lim; __u64 qgroups[0]; }; +BUILD_ASSERT(sizeof(struct btrfs_qgroup_inherit) == 72); struct btrfs_ioctl_qgroup_limit_args { __u64 qgroupid; struct btrfs_qgroup_limit lim; }; +BUILD_ASSERT(sizeof(struct btrfs_ioctl_qgroup_limit_args) == 48); #define BTRFS_SUBVOL_NAME_MAX 4039 struct btrfs_ioctl_vol_args_v2 { @@ -97,6 +107,7 @@ struct btrfs_ioctl_vol_args_v2 { __u64 devid; }; }; +BUILD_ASSERT(sizeof(struct btrfs_ioctl_vol_args_v2) == 4096); /* * structure to report errors and progress to userspace, either as a @@ -145,6 +156,7 @@ struct btrfs_ioctl_scrub_args { /* pad to 1k */ __u64 unused[(1024-32-sizeof(struct btrfs_scrub_progress))/8]; }; +BUILD_ASSERT(sizeof(struct btrfs_ioctl_scrub_args) == 1024); #define BTRFS_IOCTL_DEV_REPLACE_CONT_READING_FROM_SRCDEV_MODE_ALWAYS 0 #define BTRFS_IOCTL_DEV_REPLACE_CONT_READING_FROM_SRCDEV_MODE_AVOID 1 @@ -155,6 +167,7 @@ struct btrfs_ioctl_dev_replace_start_params { __u8 srcdev_name[BTRFS_DEVICE_PATH_NAME_MAX + 1]; /* in */ __u8 tgtdev_name[BTRFS_DEVICE_PATH_NAME_MAX + 1]; /* in */ }; +BUILD_ASSERT(sizeof(struct btrfs_ioctl_dev_replace_start_params) == 2072); #define BTRFS_IOCTL_DEV_REPLACE_STATE_NEVER_STARTED 0 #define BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED 1 @@ -169,6 +182,7 @@ struct btrfs_ioctl_dev_replace_status_params { __u64 num_write_errors; /* out */ __u64 num_uncorrectable_read_errors; /* out */ }; +BUILD_ASSERT(sizeof(struct btrfs_ioctl_dev_replace_status_params) == 48); #define BTRFS_IOCTL_DEV_REPLACE_CMD_START 0 #define BTRFS_IOCTL_DEV_REPLACE_CMD_STATUS 1 @@ -189,6 +203,7 @@ struct btrfs_ioctl_dev_replace_args { __u64 spare[64]; }; +BUILD_ASSERT(sizeof(struct btrfs_ioctl_dev_replace_args) == 2600); struct btrfs_ioctl_dev_info_args { __u64 devid; /* in/out */ @@ -198,6 +213,7 @@ struct btrfs_ioctl_dev_info_args { __u64 unused[379]; /* pad to 4k */ __u8 path[BTRFS_DEVICE_PATH_NAME_MAX]; /* out */ }; +BUILD_ASSERT(sizeof(struct btrfs_ioctl_dev_info_args) == 4096); struct btrfs_ioctl_fs_info_args { __u64 max_id; /* out */ @@ -209,12 +225,14 @@ struct btrfs_ioctl_fs_info_args { __u32 reserved32; __u64 reserved[122]; /* pad to 1k */ }; +BUILD_ASSERT(sizeof(struct btrfs_ioctl_fs_info_args) == 1024); struct btrfs_ioctl_feature_flags { __u64 compat_flags; __u64 compat_ro_flags; __u64 incompat_flags; }; +BUILD_ASSERT(sizeof(struct btrfs_ioctl_feature_flags) == 24); /* balance control ioctl modes */ #define BTRFS_BALANCE_CTL_PAUSE 1 @@ -292,6 +310,7 @@ struct btrfs_ioctl_balance_args { __u64 unused[72]; /* pad to 1k */ }; +BUILD_ASSERT(sizeof(struct btrfs_ioctl_balance_args) == 1024); #define BTRFS_INO_LOOKUP_PATH_MAX 4080 struct btrfs_ioctl_ino_lookup_args { @@ -299,6 +318,7 @@ struct btrfs_ioctl_ino_lookup_args { __u64 objectid; char name[BTRFS_INO_LOOKUP_PATH_MAX]; }; +BUILD_ASSERT(sizeof(struct btrfs_ioctl_ino_lookup_args) == 4096); struct btrfs_ioctl_search_key { /* which root are we searching. 0 is the tree of tree roots */ @@ -366,6 +386,7 @@ struct btrfs_ioctl_search_args_v2 { * to store item */ __u64 buf[0]; /* out - found items */ }; +BUILD_ASSERT(sizeof(struct btrfs_ioctl_search_args_v2) == 112); /* With a @src_length of zero, the range from @src_offset->EOF is cloned! */ struct btrfs_ioctl_clone_range_args { @@ -373,6 +394,7 @@ struct btrfs_ioctl_clone_range_args { __u64 src_offset, src_length; __u64 dest_offset; }; +BUILD_ASSERT(sizeof(struct btrfs_ioctl_clone_range_args) == 32); /* flags for the defrag range ioctl */ #define BTRFS_DEFRAG_RANGE_COMPRESS 1 @@ -402,6 +424,7 @@ struct btrfs_ioctl_same_args { __u32 reserved2; struct btrfs_ioctl_same_extent_info info[0]; }; +BUILD_ASSERT(sizeof(struct btrfs_ioctl_same_args) == 24); struct btrfs_ioctl_defrag_range_args { /* start of the defrag operation */ @@ -433,6 +456,7 @@ struct btrfs_ioctl_defrag_range_args { /* spare for later */ __u32 unused[4]; }; +BUILD_ASSERT(sizeof(struct btrfs_ioctl_defrag_range_args) == 48); struct btrfs_ioctl_space_info { __u64 flags; @@ -445,6 +469,7 @@ struct btrfs_ioctl_space_args { __u64 total_spaces; struct btrfs_ioctl_space_info spaces[0]; }; +BUILD_ASSERT(sizeof(struct btrfs_ioctl_space_args) == 16); struct btrfs_data_container { __u32 bytes_left; /* out -- bytes not needed to deliver output */ @@ -461,6 +486,7 @@ struct btrfs_ioctl_ino_path_args { /* struct btrfs_data_container *fspath; out */ __u64 fspath; /* out */ }; +BUILD_ASSERT(sizeof(struct btrfs_ioctl_ino_path_args) == 56); struct btrfs_ioctl_logical_ino_args { __u64 logical; /* in */ @@ -500,8 +526,9 @@ struct btrfs_ioctl_get_dev_stats { /* out values: */ __u64 values[BTRFS_DEV_STAT_VALUES_MAX]; - __u64 unused[128 - 2 - BTRFS_DEV_STAT_VALUES_MAX]; /* pad to 1k */ + __u64 unused[128 - 2 - BTRFS_DEV_STAT_VALUES_MAX]; /* pad to 1k + 8B */ }; +BUILD_ASSERT(sizeof(struct btrfs_ioctl_get_dev_stats) == 1032); /* BTRFS_IOC_SNAP_CREATE is no longer used by the btrfs command */ #define BTRFS_QUOTA_CTL_ENABLE 1 @@ -511,12 +538,14 @@ struct btrfs_ioctl_quota_ctl_args { __u64 cmd; __u64 status; }; +BUILD_ASSERT(sizeof(struct btrfs_ioctl_quota_ctl_args) == 16); struct btrfs_ioctl_quota_rescan_args { __u64 flags; __u64 progress; __u64 reserved[6]; }; +BUILD_ASSERT(sizeof(struct btrfs_ioctl_quota_rescan_args) == 64); struct btrfs_ioctl_qgroup_assign_args { __u64 assign; @@ -528,6 +557,8 @@ struct btrfs_ioctl_qgroup_create_args { __u64 create; __u64 qgroupid; }; +BUILD_ASSERT(sizeof(struct btrfs_ioctl_qgroup_create_args) == 16); + struct btrfs_ioctl_timespec { __u64 sec; __u32 nsec; @@ -542,6 +573,39 @@ struct btrfs_ioctl_received_subvol_args { __u64 flags; /* in */ __u64 reserved[16]; /* in */ }; +BUILD_ASSERT(sizeof(struct btrfs_ioctl_received_subvol_args) == 200); + +/* + * If we have a 32-bit userspace and 64-bit kernel, then the UAPI + * structures are incorrect, as the timespec structure from userspace + * is 4 bytes too small. We define these alternatives here for backward + * compatibility, the kernel understands both values. + */ + +/* + * Structure size is different on 32bit and 64bit, has some padding if the + * structure is embedded. Packing makes sure the size is same on both, but will + * be misaligned on 64bit. + * + * NOTE: do not use in your code, this is for testing only + */ +struct btrfs_ioctl_timespec_32 { + __u64 sec; + __u32 nsec; +} __attribute__ ((__packed__)); + +struct btrfs_ioctl_received_subvol_args_32 { + char uuid[BTRFS_UUID_SIZE]; /* in */ + __u64 stransid; /* in */ + __u64 rtransid; /* out */ + struct btrfs_ioctl_timespec_32 stime; /* in */ + struct btrfs_ioctl_timespec_32 rtime; /* out */ + __u64 flags; /* in */ + __u64 reserved[16]; /* in */ +} __attribute__ ((__packed__)); +BUILD_ASSERT(sizeof(struct btrfs_ioctl_received_subvol_args_32) == 192); + +#define BTRFS_IOC_SET_RECEIVED_SUBVOL_32_COMPAT_DEFINED 1 /* * Caller doesn't want file data in the send stream, even if the @@ -576,6 +640,37 @@ struct btrfs_ioctl_send_args { __u64 flags; /* in */ __u64 reserved[4]; /* in */ }; +/* + * Size of structure depends on pointer width, was not caught in the early + * days. Kernel handles pointer width differences transparently. + */ +BUILD_ASSERT(sizeof(__u64 *) == 8 + ? sizeof(struct btrfs_ioctl_send_args) == 72 + : (sizeof(void *) == 4 + ? sizeof(struct btrfs_ioctl_send_args) == 68 + : 0)); + +/* + * Different pointer width leads to structure size change. Kernel should accept + * both ioctl values (derived from the structures) for backward compatibility. + * Size of this structure is same on 32bit and 64bit though. + * + * NOTE: do not use in your code, this is for testing only + */ +struct btrfs_ioctl_send_args_64 { + __s64 send_fd; /* in */ + __u64 clone_sources_count; /* in */ + union { + __u64 __user *clone_sources; /* in */ + __u64 __clone_sources_alignment; + }; + __u64 parent_root; /* in */ + __u64 flags; /* in */ + __u64 reserved[4]; /* in */ +} __attribute__((packed)); +BUILD_ASSERT(sizeof(struct btrfs_ioctl_send_args_64) == 72); + +#define BTRFS_IOC_SEND_64_COMPAT_DEFINED 1 /* Error codes as returned by the kernel */ enum btrfs_err_code { @@ -688,6 +783,17 @@ static inline char *btrfs_err_str(enum btrfs_err_code err_code) struct btrfs_ioctl_logical_ino_args) #define BTRFS_IOC_SET_RECEIVED_SUBVOL _IOWR(BTRFS_IOCTL_MAGIC, 37, \ struct btrfs_ioctl_received_subvol_args) + +#ifdef BTRFS_IOC_SET_RECEIVED_SUBVOL_32_COMPAT_DEFINED +#define BTRFS_IOC_SET_RECEIVED_SUBVOL_32 _IOWR(BTRFS_IOCTL_MAGIC, 37, \ + struct btrfs_ioctl_received_subvol_args_32) +#endif + +#ifdef BTRFS_IOC_SEND_64_COMPAT_DEFINED +#define BTRFS_IOC_SEND_64 _IOW(BTRFS_IOCTL_MAGIC, 38, \ + struct btrfs_ioctl_send_args_64) +#endif + #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) diff --git a/kerncompat.h b/kerncompat.h index 8b9a84c5..1493cad8 100644 --- a/kerncompat.h +++ b/kerncompat.h @@ -68,6 +68,14 @@ #define ULONG_MAX (~0UL) #endif +#define __token_glue(a,b,c) ___token_glue(a,b,c) +#define ___token_glue(a,b,c) a ## b ## c +#ifdef DEBUG_BUILD_CHECKS +#define BUILD_ASSERT(x) extern int __token_glue(compile_time_assert_,__LINE__,__COUNTER__)[1-2*!(x)] __attribute__((unused)) +#else +#define BUILD_ASSERT(x) +#endif + #ifndef BTRFS_DISABLE_BACKTRACE #define MAX_BACKTRACE 16 static inline void print_trace(void) @@ -78,26 +86,35 @@ static inline void print_trace(void) size = backtrace(array, MAX_BACKTRACE); backtrace_symbols_fd(array, size, 2); } +#endif -static inline void assert_trace(const char *assertion, const char *filename, - const char *func, unsigned line, int val) +static inline void warning_trace(const char *assertion, const char *filename, + const char *func, unsigned line, long val) { - if (val) + if (!val) return; - if (assertion) - fprintf(stderr, "%s:%d: %s: Assertion `%s` failed.\n", - filename, line, func, assertion); - else - fprintf(stderr, "%s:%d: %s: Assertion failed.\n", filename, - line, func); + fprintf(stderr, + "%s:%d: %s: Warning: assertion `%s` failed, value %ld\n", + filename, line, func, assertion, val); +#ifndef BTRFS_DISABLE_BACKTRACE print_trace(); - exit(1); +#endif } -#define BUG() assert_trace(NULL, __FILE__, __func__, __LINE__, 0) -#else -#define BUG() assert(0) +static inline void bugon_trace(const char *assertion, const char *filename, + const char *func, unsigned line, long val) +{ + if (!val) + return; + fprintf(stderr, + "%s:%d: %s: BUG_ON `%s` triggered, value %ld\n", + filename, line, func, assertion, val); +#ifndef BTRFS_DISABLE_BACKTRACE + print_trace(); #endif + abort(); + exit(1); +} #ifdef __CHECKER__ #define __force __attribute__((force)) @@ -236,11 +253,16 @@ static inline long PTR_ERR(const void *ptr) return (long) ptr; } -static inline long IS_ERR(const void *ptr) +static inline int IS_ERR(const void *ptr) { return IS_ERR_VALUE((unsigned long)ptr); } +static inline int IS_ERR_OR_NULL(const void *ptr) +{ + return !ptr || IS_ERR(ptr); +} + /* * This looks more complex than it should be. But we need to * get the type for the ~ right in round_down (it needs to be @@ -269,19 +291,26 @@ static inline long IS_ERR(const void *ptr) #define vfree(x) free(x) #ifndef BTRFS_DISABLE_BACKTRACE -#define BUG_ON(c) assert_trace(#c, __FILE__, __func__, __LINE__, !(c)) -#else -#define BUG_ON(c) assert(!(c)) -#endif - -#define WARN_ON(c) BUG_ON(c) - -#ifndef BTRFS_DISABLE_BACKTRACE -#define ASSERT(c) assert_trace(#c, __FILE__, __func__, __LINE__, (c)) +static inline void assert_trace(const char *assertion, const char *filename, + const char *func, unsigned line, long val) +{ + if (val) + return; + fprintf(stderr, + "%s:%d: %s: Assertion `%s` failed, value %ld\n", + filename, line, func, assertion, val); + abort(); + exit(1); +} +#define ASSERT(c) assert_trace(#c, __FILE__, __func__, __LINE__, (long)(c)) #else #define ASSERT(c) assert(c) #endif +#define BUG_ON(c) bugon_trace(#c, __FILE__, __func__, __LINE__, (long)(c)) +#define BUG() BUG_ON(1) +#define WARN_ON(c) warning_trace(#c, __FILE__, __func__, __LINE__, (long)(c)) + #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) diff --git a/bitops.h b/kernel-lib/bitops.h index 5b35f9fc..5b35f9fc 100644 --- a/bitops.h +++ b/kernel-lib/bitops.h diff --git a/crc32c.c b/kernel-lib/crc32c.c index dfa4e6c1..29fd01d4 100644 --- a/crc32c.c +++ b/kernel-lib/crc32c.c @@ -218,5 +218,9 @@ u32 __crc32c_le(u32 crc, unsigned char const *data, size_t length) u32 crc32c_le(u32 crc, unsigned char const *data, size_t length) { + /* Use by-byte access for unaligned buffers */ + if ((unsigned long)data % sizeof(unsigned long)) + return __crc32c_le(crc, data, length); + return crc_function(crc, data, length); } diff --git a/crc32c.h b/kernel-lib/crc32c.h index c552ef6e..c552ef6e 100644 --- a/crc32c.h +++ b/kernel-lib/crc32c.h diff --git a/interval_tree_generic.h b/kernel-lib/interval_tree_generic.h index e26c7322..e26c7322 100644 --- a/interval_tree_generic.h +++ b/kernel-lib/interval_tree_generic.h diff --git a/list.h b/kernel-lib/list.h index db7a58c7..db7a58c7 100644 --- a/list.h +++ b/kernel-lib/list.h diff --git a/list_sort.c b/kernel-lib/list_sort.c index 9e2c2024..9e2c2024 100644 --- a/list_sort.c +++ b/kernel-lib/list_sort.c diff --git a/list_sort.h b/kernel-lib/list_sort.h index 987cd5c4..987cd5c4 100644 --- a/list_sort.h +++ b/kernel-lib/list_sort.h diff --git a/radix-tree.c b/kernel-lib/radix-tree.c index f259ab56..f259ab56 100644 --- a/radix-tree.c +++ b/kernel-lib/radix-tree.c diff --git a/radix-tree.h b/kernel-lib/radix-tree.h index bf96d839..bf96d839 100644 --- a/radix-tree.h +++ b/kernel-lib/radix-tree.h diff --git a/rbtree.c b/kernel-lib/rbtree.c index 92590a57..92590a57 100644 --- a/rbtree.c +++ b/kernel-lib/rbtree.c diff --git a/rbtree.h b/kernel-lib/rbtree.h index 47b662a3..47b662a3 100644 --- a/rbtree.h +++ b/kernel-lib/rbtree.h diff --git a/rbtree_augmented.h b/kernel-lib/rbtree_augmented.h index 5d269784..5d269784 100644 --- a/rbtree_augmented.h +++ b/kernel-lib/rbtree_augmented.h diff --git a/library-test.c b/library-test.c index 142188a7..9d14bbf9 100644 --- a/library-test.c +++ b/library-test.c @@ -19,6 +19,7 @@ #include "kerncompat.h" #include "version.h" #include "send-stream.h" +#include "btrfs-list.h" /* * Reduced code snippet from snapper.git/snapper/Btrfs.cc @@ -62,8 +63,15 @@ static int test_send_stream_api() { return ret; } +static int test_list_rootid() { + u64 treeid; + + return btrfs_list_get_path_rootid(-1, &treeid); +} + int main() { test_send_stream_api(); + test_list_rootid(); return 0; } @@ -48,8 +48,8 @@ static u64 index_cnt = 2; static int verbose = 1; struct directory_name_entry { - char *dir_name; - char *path; + const char *dir_name; + const char *path; ino_t inum; struct list_head list; }; @@ -344,28 +344,34 @@ static int create_data_reloc_tree(struct btrfs_trans_handle *trans, static void print_usage(int ret) { - printf("usage: mkfs.btrfs [options] dev [ dev ... ]\n"); - printf("options:\n"); - printf("\t-A|--alloc-start START the offset to start the FS\n"); - printf("\t-b|--byte-count SIZE total number of bytes in the FS\n"); + printf("Usage: mkfs.btrfs [options] dev [ dev ... ]\n"); + printf("Options:\n"); + printf(" allocation profiles:\n"); printf("\t-d|--data PROFILE data profile, raid0, raid1, raid5, raid6, raid10, dup or single\n"); - printf("\t-f|--force force overwrite of existing filesystem\n"); - printf("\t-l|--leafsize SIZE deprecated, alias for nodesize\n"); - printf("\t-L|--label LABEL set a label\n"); - printf("\t-m|--metadata PROFILE metadata profile, values like data profile\n"); + printf("\t-m|--metadata PROFILE metadata profile, values like for data profile\n"); printf("\t-M|--mixed mix metadata and data together\n"); + printf(" features:\n"); printf("\t-n|--nodesize SIZE size of btree nodes\n"); - printf("\t-s|--sectorsize SIZE min block allocation (may not mountable by current kernel)\n"); - printf("\t-r|--rootdir DIR the source directory\n"); + printf("\t-s|--sectorsize SIZE data block size (may not be mountable by current kernel)\n"); + printf("\t-O|--features LIST comma separated list of filesystem features (use '-O list-all' to list features)\n"); + printf("\t-L|--label LABEL set the filesystem label\n"); + printf("\t-U|--uuid UUID specify the filesystem UUID (must be unique)\n"); + printf(" creation:\n"); + printf("\t-b|--byte-count SIZE set filesystem size to SIZE (on the first device)\n"); + printf("\t-r|--rootdir DIR copy files from DIR to the image root directory\n"); printf("\t-K|--nodiscard do not perform whole device TRIM\n"); - printf("\t-O|--features LIST comma separated list of filesystem features, use '-O list-all' to list features\n"); - printf("\t-U|--uuid UUID specify the filesystem UUID\n"); + printf("\t-f|--force force overwrite of existing filesystem\n"); + printf(" general:\n"); printf("\t-q|--quiet no messages except errors\n"); printf("\t-V|--version print the mkfs.btrfs version and exit\n"); + printf("\t--help print this help and exit\n"); + printf(" deprecated:\n"); + printf("\t-A|--alloc-start START the offset to start the filesystem\n"); + printf("\t-l|--leafsize SIZE deprecated, alias for nodesize\n"); exit(ret); } -static u64 parse_profile(char *s) +static u64 parse_profile(const char *s) { if (strcasecmp(s, "raid0") == 0) { return BTRFS_BLOCK_GROUP_RAID0; @@ -389,7 +395,7 @@ static u64 parse_profile(char *s) return 0; } -static char *parse_label(char *input) +static char *parse_label(const char *input) { int len = strlen(input); @@ -415,7 +421,7 @@ static int add_directory_items(struct btrfs_trans_handle *trans, location.objectid = objectid; location.offset = 0; - btrfs_set_key_type(&location, BTRFS_INODE_ITEM_KEY); + location.type = BTRFS_INODE_ITEM_KEY; if (S_ISDIR(st->st_mode)) filetype = BTRFS_FT_DIR; @@ -494,11 +500,11 @@ static int fill_inode_item(struct btrfs_trans_handle *trans, static int directory_select(const struct direct *entry) { - if ((strncmp(entry->d_name, ".", entry->d_reclen) == 0) || - (strncmp(entry->d_name, "..", entry->d_reclen) == 0)) + if (entry->d_name[0] == '.' && + (entry->d_name[1] == 0 || + (entry->d_name[1] == '.' && entry->d_name[2] == 0))) return 0; - else - return 1; + return 1; } static void free_namelist(struct direct **files, int count) @@ -513,7 +519,7 @@ static void free_namelist(struct direct **files, int count) free(files); } -static u64 calculate_dir_inode_size(char *dirname) +static u64 calculate_dir_inode_size(const char *dirname) { int count, i; struct direct **files, *cur_file; @@ -534,12 +540,11 @@ static u64 calculate_dir_inode_size(char *dirname) static int add_inode_items(struct btrfs_trans_handle *trans, struct btrfs_root *root, - struct stat *st, char *name, + struct stat *st, const char *name, u64 self_objectid, ino_t parent_inum, int dir_index_cnt, struct btrfs_inode_item *inode_ret) { int ret; - struct btrfs_key inode_key; struct btrfs_inode_item btrfs_inode; u64 objectid; u64 inode_size = 0; @@ -552,10 +557,6 @@ static int add_inode_items(struct btrfs_trans_handle *trans, btrfs_set_stack_inode_size(&btrfs_inode, inode_size); } - inode_key.objectid = objectid; - inode_key.offset = 0; - btrfs_set_key_type(&inode_key, BTRFS_INODE_ITEM_KEY); - ret = btrfs_insert_inode(trans, root, objectid, &btrfs_inode); *inode_ret = btrfs_inode; @@ -779,7 +780,7 @@ end: return ret; } -static char *make_path(char *dir, char *name) +static char *make_path(const char *dir, const char *name) { char *path; @@ -794,7 +795,7 @@ static char *make_path(char *dir, char *name) } static int traverse_directory(struct btrfs_trans_handle *trans, - struct btrfs_root *root, char *dir_name, + struct btrfs_root *root, const char *dir_name, struct directory_name_entry *dir_head, int out_fd) { int ret = 0; @@ -808,7 +809,7 @@ static int traverse_directory(struct btrfs_trans_handle *trans, struct direct *cur_file; ino_t parent_inum, cur_inum; ino_t highest_inum = 0; - char *parent_dir_name; + const char *parent_dir_name; char real_path[PATH_MAX]; struct btrfs_path path; struct extent_buffer *leaf; @@ -835,7 +836,7 @@ static int traverse_directory(struct btrfs_trans_handle *trans, root_dir_key.objectid = btrfs_root_dirid(&root->root_item); root_dir_key.offset = 0; - btrfs_set_key_type(&root_dir_key, BTRFS_INODE_ITEM_KEY); + root_dir_key.type = BTRFS_INODE_ITEM_KEY; ret = btrfs_lookup_inode(trans, root, &path, &root_dir_key, 1); if (ret) { error("failed to lookup root dir: %d", ret); @@ -905,8 +906,9 @@ static int traverse_directory(struct btrfs_trans_handle *trans, if (ret == -EEXIST) { if (st.st_nlink <= 1) { error( - "item %s already exists but has wrong st_nlink %ld <= 1", - cur_file->d_name, st.st_nlink); + "item %s already exists but has wrong st_nlink %lu <= 1", + cur_file->d_name, + (unsigned long)st.st_nlink); goto fail; } continue; @@ -976,15 +978,6 @@ fail_no_dir: goto out; } -static int open_target(char *output_name) -{ - int output_fd; - output_fd = open(output_name, O_CREAT | O_RDWR, - S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); - - return output_fd; -} - static int create_chunks(struct btrfs_trans_handle *trans, struct btrfs_root *root, u64 num_of_meta_chunks, u64 size_of_data, @@ -1031,7 +1024,8 @@ static int create_chunks(struct btrfs_trans_handle *trans, return ret; } -static int make_image(char *source_dir, struct btrfs_root *root, int out_fd) +static int make_image(const char *source_dir, struct btrfs_root *root, + int out_fd) { int ret; struct btrfs_trans_handle *trans; @@ -1091,7 +1085,7 @@ static int ftw_add_entry_size(const char *fpath, const struct stat *st, return 0; } -static u64 size_sourcedir(char *dir_name, u64 sectorsize, +static u64 size_sourcedir(const char *dir_name, u64 sectorsize, u64 *num_of_meta_chunks_ret, u64 *size_of_data_ret) { u64 dir_size = 0; @@ -1322,15 +1316,10 @@ static int cleanup_temp_chunks(struct btrfs_fs_info *fs_info, struct btrfs_root *root = fs_info->extent_root; struct btrfs_key key; struct btrfs_key found_key; - struct btrfs_path *path; + struct btrfs_path path; int ret = 0; - path = btrfs_alloc_path(); - if (!path) { - ret = -ENOMEM; - goto out; - } - + btrfs_init_path(&path); trans = btrfs_start_transaction(root, 1); key.objectid = 0; @@ -1342,32 +1331,32 @@ static int cleanup_temp_chunks(struct btrfs_fs_info *fs_info, * as the rest of the loop may modify the tree, we need to * start a new search each time. */ - ret = btrfs_search_slot(trans, root, &key, path, 0, 0); + ret = btrfs_search_slot(trans, root, &key, &path, 0, 0); if (ret < 0) goto out; - btrfs_item_key_to_cpu(path->nodes[0], &found_key, - path->slots[0]); + btrfs_item_key_to_cpu(path.nodes[0], &found_key, + path.slots[0]); if (found_key.objectid < key.objectid) goto out; if (found_key.type != BTRFS_BLOCK_GROUP_ITEM_KEY) { - ret = next_block_group(root, path); + ret = next_block_group(root, &path); if (ret < 0) goto out; if (ret > 0) { ret = 0; goto out; } - btrfs_item_key_to_cpu(path->nodes[0], &found_key, - path->slots[0]); + btrfs_item_key_to_cpu(path.nodes[0], &found_key, + path.slots[0]); } - bgi = btrfs_item_ptr(path->nodes[0], path->slots[0], + bgi = btrfs_item_ptr(path.nodes[0], path.slots[0], struct btrfs_block_group_item); - if (is_temp_block_group(path->nodes[0], bgi, + if (is_temp_block_group(path.nodes[0], bgi, data_profile, meta_profile, sys_profile)) { - u64 flags = btrfs_disk_block_group_flags(path->nodes[0], + u64 flags = btrfs_disk_block_group_flags(path.nodes[0], bgi); ret = btrfs_free_block_group(trans, fs_info, @@ -1389,13 +1378,13 @@ static int cleanup_temp_chunks(struct btrfs_fs_info *fs_info, BTRFS_BLOCK_GROUP_DATA)) alloc->mixed -= found_key.offset; } - btrfs_release_path(path); + btrfs_release_path(&path); key.objectid = found_key.objectid + found_key.offset; } out: if (trans) btrfs_commit_transaction(trans, root); - btrfs_free_path(path); + btrfs_release_path(&path); return ret; } @@ -1479,6 +1468,7 @@ int main(int argc, char **argv) break; case 'l': warning("--leafsize is deprecated, use --nodesize"); + /* fall through */ case 'n': nodesize = parse_size(optarg); nodesize_forced = 1; @@ -1636,6 +1626,12 @@ int main(int argc, char **argv) features)) exit(1); + if (sectorsize < sizeof(struct btrfs_super_block)) { + error("sectorsize smaller than superblock: %u < %zu", + sectorsize, sizeof(struct btrfs_super_block)); + exit(1); + } + /* Check device/block_count after the nodesize is determined */ if (block_count && block_count < btrfs_min_dev_size(nodesize)) { error("size %llu is too small to make a usable filesystem", @@ -1697,7 +1693,8 @@ int main(int argc, char **argv) exit(1); } } else { - fd = open_target(file); + fd = open(file, O_CREAT | O_RDWR, + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); if (fd < 0) { error("unable to open %s: %s", file, strerror(errno)); exit(1); @@ -1743,7 +1740,7 @@ int main(int argc, char **argv) mkfs_cfg.stripesize = stripesize; mkfs_cfg.features = features; - ret = make_btrfs(fd, &mkfs_cfg, NULL); + ret = make_btrfs(fd, &mkfs_cfg); if (ret) { error("error during mkfs: %s", strerror(-ret)); exit(1); @@ -1899,7 +1896,7 @@ raid_groups: char features_buf[64]; printf("Label: %s\n", label); - printf("UUID: %s\n", fs_uuid); + printf("UUID: %s\n", mkfs_cfg.fs_uuid); printf("Node size: %u\n", nodesize); printf("Sector size: %u\n", sectorsize); printf("Filesystem size: %s\n", diff --git a/print-tree.c b/print-tree.c index f97482f3..5af80e87 100644 --- a/print-tree.c +++ b/print-tree.c @@ -31,41 +31,26 @@ static void print_dir_item_type(struct extent_buffer *eb, struct btrfs_dir_item *di) { u8 type = btrfs_dir_type(eb, di); + static const char* dir_item_str[] = { + [BTRFS_FT_REG_FILE] = "FILE", + [BTRFS_FT_DIR] = "DIR", + [BTRFS_FT_CHRDEV] = "CHRDEV", + [BTRFS_FT_BLKDEV] = "BLKDEV", + [BTRFS_FT_FIFO] = "FIFO", + [BTRFS_FT_SOCK] = "SOCK", + [BTRFS_FT_SYMLINK] = "SYMLINK", + [BTRFS_FT_XATTR] = "XATTR" + }; - switch (type) { - case BTRFS_FT_REG_FILE: - printf("FILE"); - break; - case BTRFS_FT_DIR: - printf("DIR"); - break; - case BTRFS_FT_CHRDEV: - printf("CHRDEV"); - break; - case BTRFS_FT_BLKDEV: - printf("BLKDEV"); - break; - case BTRFS_FT_FIFO: - printf("FIFO"); - break; - case BTRFS_FT_SOCK: - printf("SOCK"); - break; - case BTRFS_FT_SYMLINK: - printf("SYMLINK"); - break; - case BTRFS_FT_XATTR: - printf("XATTR"); - break; - default: - printf("%u", type); - } + if (type < ARRAY_SIZE(dir_item_str) && dir_item_str[type]) + printf("%s", dir_item_str[type]); + else + printf("DIR_ITEM.%u", type); } -static int print_dir_item(struct extent_buffer *eb, struct btrfs_item *item, +static void print_dir_item(struct extent_buffer *eb, u32 size, struct btrfs_dir_item *di) { - u32 total; u32 cur = 0; u32 len; u32 name_len; @@ -73,8 +58,7 @@ static int print_dir_item(struct extent_buffer *eb, struct btrfs_item *item, char namebuf[BTRFS_NAME_LEN]; struct btrfs_disk_key location; - total = btrfs_item_size(eb, item); - while(cur < total) { + while (cur < size) { btrfs_dir_item_key(eb, di, &location); printf("\t\tlocation "); btrfs_print_key(&location); @@ -85,8 +69,10 @@ static int print_dir_item(struct extent_buffer *eb, struct btrfs_item *item, data_len = btrfs_dir_data_len(eb, di); len = (name_len <= sizeof(namebuf))? name_len: sizeof(namebuf); read_extent_buffer(eb, namebuf, (unsigned long)(di + 1), len); - printf("\t\tnamelen %u datalen %u name: %.*s\n", - name_len, data_len, len, namebuf); + printf("\t\ttransid %llu data_len %u name_len %u\n", + btrfs_dir_transid(eb, di), + data_len, name_len); + printf("\t\tname: %.*s\n", len, namebuf); if (data_len) { len = (data_len <= sizeof(namebuf))? data_len: sizeof(namebuf); read_extent_buffer(eb, namebuf, @@ -97,14 +83,11 @@ static int print_dir_item(struct extent_buffer *eb, struct btrfs_item *item, di = (struct btrfs_dir_item *)((char *)di + len); cur += len; } - return 0; } -static int print_inode_extref_item(struct extent_buffer *eb, - struct btrfs_item *item, - struct btrfs_inode_extref *extref) +static void print_inode_extref_item(struct extent_buffer *eb, u32 size, + struct btrfs_inode_extref *extref) { - u32 total; u32 cur = 0; u32 len; u32 name_len = 0; @@ -112,9 +95,7 @@ static int print_inode_extref_item(struct extent_buffer *eb, u64 parent_objid; char namebuf[BTRFS_NAME_LEN]; - total = btrfs_item_size(eb, item); - - while (cur < total) { + while (cur < size) { index = btrfs_inode_extref_index(eb, extref); name_len = btrfs_inode_extref_name_len(eb, extref); parent_objid = btrfs_inode_extref_parent(eb, extref); @@ -133,20 +114,18 @@ static int print_inode_extref_item(struct extent_buffer *eb, extref = (struct btrfs_inode_extref *)((char *)extref + len); cur += len; } - return 0; } -static int print_inode_ref_item(struct extent_buffer *eb, struct btrfs_item *item, +static void print_inode_ref_item(struct extent_buffer *eb, u32 size, struct btrfs_inode_ref *ref) { - u32 total; u32 cur = 0; u32 len; u32 name_len; u64 index; char namebuf[BTRFS_NAME_LEN]; - total = btrfs_item_size(eb, item); - while(cur < total) { + + while (cur < size) { name_len = btrfs_inode_ref_name_len(eb, ref); index = btrfs_inode_ref_index(eb, ref); len = (name_len <= sizeof(namebuf))? name_len: sizeof(namebuf); @@ -157,7 +136,6 @@ static int print_inode_ref_item(struct extent_buffer *eb, struct btrfs_item *ite ref = (struct btrfs_inode_ref *)((char *)ref + len); cur += len; } - return 0; } /* Caller should ensure sizeof(*ret)>=21 "DATA|METADATA|RAID10" */ @@ -224,12 +202,17 @@ void print_chunk(struct extent_buffer *eb, struct btrfs_chunk *chunk) char chunk_flags_str[32] = {0}; bg_flags_to_str(btrfs_chunk_type(eb, chunk), chunk_flags_str); - printf("\t\tchunk length %llu owner %llu stripe_len %llu\n", + printf("\t\tlength %llu owner %llu stripe_len %llu type %s\n", (unsigned long long)btrfs_chunk_length(eb, chunk), (unsigned long long)btrfs_chunk_owner(eb, chunk), - (unsigned long long)btrfs_chunk_stripe_len(eb, chunk)); - printf("\t\ttype %s num_stripes %d\n", - chunk_flags_str, num_stripes); + (unsigned long long)btrfs_chunk_stripe_len(eb, chunk), + chunk_flags_str); + printf("\t\tio_align %u io_width %u sector_size %u\n", + btrfs_chunk_io_align(eb, chunk), + btrfs_chunk_io_width(eb, chunk), + btrfs_chunk_sector_size(eb, chunk)); + printf("\t\tnum_stripes %hu sub_stripes %hu\n", num_stripes, + btrfs_chunk_sub_stripes(eb, chunk)); for (i = 0 ; i < num_stripes ; i++) { unsigned char dev_uuid[BTRFS_UUID_SIZE]; char str_dev_uuid[BTRFS_UUID_UNPARSED_SIZE]; @@ -241,27 +224,45 @@ void print_chunk(struct extent_buffer *eb, struct btrfs_chunk *chunk) printf("\t\t\tstripe %d devid %llu offset %llu\n", i, (unsigned long long)btrfs_stripe_devid_nr(eb, chunk, i), (unsigned long long)btrfs_stripe_offset_nr(eb, chunk, i)); - printf("\t\t\tdev uuid: %s\n", str_dev_uuid); + printf("\t\t\tdev_uuid %s\n", str_dev_uuid); } } static void print_dev_item(struct extent_buffer *eb, struct btrfs_dev_item *dev_item) { - char disk_uuid_c[BTRFS_UUID_UNPARSED_SIZE]; - u8 disk_uuid[BTRFS_UUID_SIZE]; + char uuid_str[BTRFS_UUID_UNPARSED_SIZE]; + char fsid_str[BTRFS_UUID_UNPARSED_SIZE]; + u8 uuid[BTRFS_UUID_SIZE]; + u8 fsid[BTRFS_UUID_SIZE]; - read_extent_buffer(eb, disk_uuid, + read_extent_buffer(eb, uuid, (unsigned long)btrfs_device_uuid(dev_item), BTRFS_UUID_SIZE); - uuid_unparse(disk_uuid, disk_uuid_c); - printf("\t\tdev item devid %llu " - "total_bytes %llu bytes used %Lu\n" - "\t\tdev uuid %s\n", + uuid_unparse(uuid, uuid_str); + read_extent_buffer(eb, fsid, + (unsigned long)btrfs_device_fsid(dev_item), + BTRFS_UUID_SIZE); + uuid_unparse(fsid, fsid_str); + printf("\t\tdevid %llu total_bytes %llu bytes_used %Lu\n" + "\t\tio_align %u io_width %u sector_size %u type %llu\n" + "\t\tgeneration %llu start_offset %llu dev_group %u\n" + "\t\tseek_speed %hhu bandwidth %hhu\n" + "\t\tuuid %s\n" + "\t\tfsid %s\n", (unsigned long long)btrfs_device_id(eb, dev_item), (unsigned long long)btrfs_device_total_bytes(eb, dev_item), (unsigned long long)btrfs_device_bytes_used(eb, dev_item), - disk_uuid_c); + btrfs_device_io_align(eb, dev_item), + btrfs_device_io_width(eb, dev_item), + btrfs_device_sector_size(eb, dev_item), + (unsigned long long)btrfs_device_type(eb, dev_item), + (unsigned long long)btrfs_device_generation(eb, dev_item), + (unsigned long long)btrfs_device_start_offset(eb, dev_item), + btrfs_device_group(eb, dev_item), + btrfs_device_seek_speed(eb, dev_item), + btrfs_device_bandwidth(eb, dev_item), + uuid_str, fsid_str); } static void print_uuids(struct extent_buffer *eb) @@ -302,6 +303,16 @@ static void compress_type_to_str(u8 compress_type, char *ret) } } +static const char* file_extent_type_to_str(u8 type) +{ + switch (type) { + case BTRFS_FILE_EXTENT_INLINE: return "inline"; + case BTRFS_FILE_EXTENT_PREALLOC: return "prealloc"; + case BTRFS_FILE_EXTENT_REG: return "regular"; + default: return "unknown"; + } +} + static void print_file_extent_item(struct extent_buffer *eb, struct btrfs_item *item, int slot, @@ -313,12 +324,16 @@ static void print_file_extent_item(struct extent_buffer *eb, compress_type_to_str(btrfs_file_extent_compression(eb, fi), compress_str); + printf("\t\tgeneration %llu type %hhu (%s)\n", + btrfs_file_extent_generation(eb, fi), + extent_type, file_extent_type_to_str(extent_type)); + if (extent_type == BTRFS_FILE_EXTENT_INLINE) { - printf("\t\tinline extent data size %u " - "ram %u compress(%s)\n", - btrfs_file_extent_inline_item_len(eb, item), - btrfs_file_extent_inline_len(eb, slot, fi), - compress_str); + printf("\t\tinline extent data size %u ram_bytes %u compression %hhu (%s)\n", + btrfs_file_extent_inline_item_len(eb, item), + btrfs_file_extent_inline_len(eb, slot, fi), + btrfs_file_extent_compression(eb, fi), + compress_str); return; } if (extent_type == BTRFS_FILE_EXTENT_PREALLOC) { @@ -337,7 +352,9 @@ static void print_file_extent_item(struct extent_buffer *eb, (unsigned long long)btrfs_file_extent_offset(eb, fi), (unsigned long long)btrfs_file_extent_num_bytes(eb, fi), (unsigned long long)btrfs_file_extent_ram_bytes(eb, fi)); - printf("\t\textent compression(%s)\n", compress_str); + printf("\t\textent compression %hhu (%s)\n", + btrfs_file_extent_compression(eb, fi), + compress_str); } /* Caller should ensure sizeof(*ret) >= 16("DATA|TREE_BLOCK") */ @@ -470,7 +487,7 @@ static void print_extent_ref_v0(struct extent_buffer *eb, int slot) } #endif -static void print_root_ref(struct extent_buffer *leaf, int slot, char *tag) +static void print_root_ref(struct extent_buffer *leaf, int slot, const char *tag) { struct btrfs_root_ref *ref; char namebuf[BTRFS_NAME_LEN]; @@ -485,15 +502,14 @@ static void print_root_ref(struct extent_buffer *leaf, int slot, char *tag) namelen, namebuf); } -static int count_bytes(void *buf, int len, char b) +static int empty_uuid(const u8 *uuid) { - int cnt = 0; int i; - for (i = 0; i < len; i++) { - if (((char*)buf)[i] == b) - cnt++; - } - return cnt; + + for (i = 0; i < BTRFS_UUID_SIZE; i++) + if (uuid[i]) + return 0; + return 1; } /* @@ -514,6 +530,7 @@ static void print_root(struct extent_buffer *leaf, int slot) int len; char uuid_str[BTRFS_UUID_UNPARSED_SIZE]; char flags_str[32] = {0}; + struct btrfs_key drop_key; ri = btrfs_item_ptr(leaf, slot, struct btrfs_root_item); len = btrfs_item_size_nr(leaf, slot); @@ -522,24 +539,27 @@ static void print_root(struct extent_buffer *leaf, int slot) read_extent_buffer(leaf, &root_item, (unsigned long)ri, len); root_flags_to_str(btrfs_root_flags(&root_item), flags_str); - printf("\t\troot data bytenr %llu level %d dirid %llu refs %u gen %llu lastsnap %llu\n", + printf("\t\tgeneration %llu root_dirid %llu bytenr %llu level %hhu refs %u\n", + (unsigned long long)btrfs_root_generation(&root_item), + (unsigned long long)btrfs_root_dirid(&root_item), (unsigned long long)btrfs_root_bytenr(&root_item), btrfs_root_level(&root_item), - (unsigned long long)btrfs_root_dirid(&root_item), - btrfs_root_refs(&root_item), - (unsigned long long)btrfs_root_generation(&root_item), - (unsigned long long)btrfs_root_last_snapshot(&root_item)); - printf("\t\tflags 0x%llx(%s)\n", btrfs_root_flags(&root_item), - flags_str); + btrfs_root_refs(&root_item)); + printf("\t\tlastsnap %llu byte_limit %llu bytes_used %llu flags 0x%llx(%s)\n", + (unsigned long long)btrfs_root_last_snapshot(&root_item), + (unsigned long long)btrfs_root_limit(&root_item), + (unsigned long long)btrfs_root_used(&root_item), + (unsigned long long)btrfs_root_flags(&root_item), + flags_str); if (root_item.generation == root_item.generation_v2) { uuid_unparse(root_item.uuid, uuid_str); printf("\t\tuuid %s\n", uuid_str); - if (count_bytes(root_item.parent_uuid, BTRFS_UUID_SIZE, 0) != BTRFS_UUID_SIZE) { + if (!empty_uuid(root_item.parent_uuid)) { uuid_unparse(root_item.parent_uuid, uuid_str); printf("\t\tparent_uuid %s\n", uuid_str); } - if (count_bytes(root_item.received_uuid, BTRFS_UUID_SIZE, 0) != BTRFS_UUID_SIZE) { + if (!empty_uuid(root_item.received_uuid)) { uuid_unparse(root_item.received_uuid, uuid_str); printf("\t\treceived_uuid %s\n", uuid_str); } @@ -551,14 +571,11 @@ static void print_root(struct extent_buffer *leaf, int slot) btrfs_root_rtransid(&root_item)); } } - if (btrfs_root_refs(&root_item) == 0) { - struct btrfs_key drop_key; - btrfs_disk_key_to_cpu(&drop_key, - &root_item.drop_progress); - printf("\t\tdrop "); - btrfs_print_key(&root_item.drop_progress); - printf(" level %d\n", root_item.drop_level); - } + + btrfs_disk_key_to_cpu(&drop_key, &root_item.drop_progress); + printf("\t\tdrop "); + btrfs_print_key(&root_item.drop_progress); + printf(" level %hhu\n", root_item.drop_level); } static void print_free_space_header(struct extent_buffer *leaf, int slot) @@ -579,132 +596,58 @@ static void print_free_space_header(struct extent_buffer *leaf, int slot) void print_key_type(FILE *stream, u64 objectid, u8 type) { + static const char* key_to_str[256] = { + [BTRFS_INODE_ITEM_KEY] = "INODE_ITEM", + [BTRFS_INODE_REF_KEY] = "INODE_REF", + [BTRFS_INODE_EXTREF_KEY] = "INODE_EXTREF", + [BTRFS_DIR_ITEM_KEY] = "DIR_ITEM", + [BTRFS_DIR_INDEX_KEY] = "DIR_INDEX", + [BTRFS_DIR_LOG_ITEM_KEY] = "DIR_LOG_ITEM", + [BTRFS_DIR_LOG_INDEX_KEY] = "DIR_LOG_INDEX", + [BTRFS_XATTR_ITEM_KEY] = "XATTR_ITEM", + [BTRFS_ORPHAN_ITEM_KEY] = "ORPHAN_ITEM", + [BTRFS_ROOT_ITEM_KEY] = "ROOT_ITEM", + [BTRFS_ROOT_REF_KEY] = "ROOT_REF", + [BTRFS_ROOT_BACKREF_KEY] = "ROOT_BACKREF", + [BTRFS_EXTENT_ITEM_KEY] = "EXTENT_ITEM", + [BTRFS_METADATA_ITEM_KEY] = "METADATA_ITEM", + [BTRFS_TREE_BLOCK_REF_KEY] = "TREE_BLOCK_REF", + [BTRFS_SHARED_BLOCK_REF_KEY] = "SHARED_BLOCK_REF", + [BTRFS_EXTENT_DATA_REF_KEY] = "EXTENT_DATA_REF", + [BTRFS_SHARED_DATA_REF_KEY] = "SHARED_DATA_REF", + [BTRFS_EXTENT_REF_V0_KEY] = "EXTENT_REF_V0", + [BTRFS_CSUM_ITEM_KEY] = "CSUM_ITEM", + [BTRFS_EXTENT_CSUM_KEY] = "EXTENT_CSUM", + [BTRFS_EXTENT_DATA_KEY] = "EXTENT_DATA", + [BTRFS_BLOCK_GROUP_ITEM_KEY] = "BLOCK_GROUP_ITEM", + [BTRFS_FREE_SPACE_INFO_KEY] = "FREE_SPACE_INFO", + [BTRFS_FREE_SPACE_EXTENT_KEY] = "FREE_SPACE_EXTENT", + [BTRFS_FREE_SPACE_BITMAP_KEY] = "FREE_SPACE_BITMAP", + [BTRFS_CHUNK_ITEM_KEY] = "CHUNK_ITEM", + [BTRFS_DEV_ITEM_KEY] = "DEV_ITEM", + [BTRFS_DEV_EXTENT_KEY] = "DEV_EXTENT", + [BTRFS_TEMPORARY_ITEM_KEY] = "TEMPORARY_ITEM", + [BTRFS_DEV_REPLACE_KEY] = "DEV_REPLACE", + [BTRFS_STRING_ITEM_KEY] = "STRING_ITEM", + [BTRFS_QGROUP_STATUS_KEY] = "QGROUP_STATUS", + [BTRFS_QGROUP_RELATION_KEY] = "QGROUP_RELATION", + [BTRFS_QGROUP_INFO_KEY] = "QGROUP_INFO", + [BTRFS_QGROUP_LIMIT_KEY] = "QGROUP_LIMIT", + [BTRFS_PERSISTENT_ITEM_KEY] = "PERSISTENT_ITEM", + [BTRFS_UUID_KEY_SUBVOL] = "UUID_KEY_SUBVOL", + [BTRFS_UUID_KEY_RECEIVED_SUBVOL] = "UUID_KEY_RECEIVED_SUBVOL", + }; + if (type == 0 && objectid == BTRFS_FREE_SPACE_OBJECTID) { fprintf(stream, "UNTYPED"); return; } - switch (type) { - case BTRFS_INODE_ITEM_KEY: - fprintf(stream, "INODE_ITEM"); - break; - case BTRFS_INODE_REF_KEY: - fprintf(stream, "INODE_REF"); - break; - case BTRFS_INODE_EXTREF_KEY: - fprintf(stream, "INODE_EXTREF"); - break; - case BTRFS_DIR_ITEM_KEY: - fprintf(stream, "DIR_ITEM"); - break; - case BTRFS_DIR_INDEX_KEY: - fprintf(stream, "DIR_INDEX"); - break; - case BTRFS_DIR_LOG_ITEM_KEY: - fprintf(stream, "DIR_LOG_ITEM"); - break; - case BTRFS_DIR_LOG_INDEX_KEY: - fprintf(stream, "DIR_LOG_INDEX"); - break; - case BTRFS_XATTR_ITEM_KEY: - fprintf(stream, "XATTR_ITEM"); - break; - case BTRFS_ORPHAN_ITEM_KEY: - fprintf(stream, "ORPHAN_ITEM"); - break; - case BTRFS_ROOT_ITEM_KEY: - fprintf(stream, "ROOT_ITEM"); - break; - case BTRFS_ROOT_REF_KEY: - fprintf(stream, "ROOT_REF"); - break; - case BTRFS_ROOT_BACKREF_KEY: - fprintf(stream, "ROOT_BACKREF"); - break; - case BTRFS_EXTENT_ITEM_KEY: - fprintf(stream, "EXTENT_ITEM"); - break; - case BTRFS_METADATA_ITEM_KEY: - fprintf(stream, "METADATA_ITEM"); - break; - case BTRFS_TREE_BLOCK_REF_KEY: - fprintf(stream, "TREE_BLOCK_REF"); - break; - case BTRFS_SHARED_BLOCK_REF_KEY: - fprintf(stream, "SHARED_BLOCK_REF"); - break; - case BTRFS_EXTENT_DATA_REF_KEY: - fprintf(stream, "EXTENT_DATA_REF"); - break; - case BTRFS_SHARED_DATA_REF_KEY: - fprintf(stream, "SHARED_DATA_REF"); - break; - case BTRFS_EXTENT_REF_V0_KEY: - fprintf(stream, "EXTENT_REF_V0"); - break; - case BTRFS_CSUM_ITEM_KEY: - fprintf(stream, "CSUM_ITEM"); - break; - case BTRFS_EXTENT_CSUM_KEY: - fprintf(stream, "EXTENT_CSUM"); - break; - case BTRFS_EXTENT_DATA_KEY: - fprintf(stream, "EXTENT_DATA"); - break; - case BTRFS_BLOCK_GROUP_ITEM_KEY: - fprintf(stream, "BLOCK_GROUP_ITEM"); - break; - case BTRFS_FREE_SPACE_INFO_KEY: - fprintf(stream, "FREE_SPACE_INFO"); - break; - case BTRFS_FREE_SPACE_EXTENT_KEY: - fprintf(stream, "FREE_SPACE_EXTENT"); - break; - case BTRFS_FREE_SPACE_BITMAP_KEY: - fprintf(stream, "FREE_SPACE_BITMAP"); - break; - case BTRFS_CHUNK_ITEM_KEY: - fprintf(stream, "CHUNK_ITEM"); - break; - case BTRFS_DEV_ITEM_KEY: - fprintf(stream, "DEV_ITEM"); - break; - case BTRFS_DEV_EXTENT_KEY: - fprintf(stream, "DEV_EXTENT"); - break; - case BTRFS_BALANCE_ITEM_KEY: - fprintf(stream, "BALANCE_ITEM"); - break; - case BTRFS_DEV_REPLACE_KEY: - fprintf(stream, "DEV_REPLACE"); - break; - case BTRFS_STRING_ITEM_KEY: - fprintf(stream, "STRING_ITEM"); - break; - case BTRFS_QGROUP_STATUS_KEY: - fprintf(stream, "QGROUP_STATUS"); - break; - case BTRFS_QGROUP_RELATION_KEY: - fprintf(stream, "QGROUP_RELATION"); - break; - case BTRFS_QGROUP_INFO_KEY: - fprintf(stream, "QGROUP_INFO"); - break; - case BTRFS_QGROUP_LIMIT_KEY: - fprintf(stream, "QGROUP_LIMIT"); - break; - case BTRFS_DEV_STATS_KEY: - fprintf(stream, "DEV_STATS"); - break; - case BTRFS_UUID_KEY_SUBVOL: - fprintf(stream, "UUID_KEY_SUBVOL"); - break; - case BTRFS_UUID_KEY_RECEIVED_SUBVOL: - fprintf(stream, "UUID_KEY_RECEIVED_SUBVOL"); - break; - default: + + if (key_to_str[type]) + fputs(key_to_str[type], stream); + else fprintf(stream, "UNKNOWN.%d", type); - }; } void print_objectid(FILE *stream, u64 objectid, u8 type) @@ -851,138 +794,237 @@ static void print_uuid_item(struct extent_buffer *l, unsigned long offset, } } -/* Caller should ensure sizeof(*ret) >= 29 "NODATASUM|NODATACOW|READONLY" */ +/* Btrfs inode flag stringification helper */ +#define STRCAT_ONE_INODE_FLAG(flags, name, empty, dst) ({ \ + if (flags & BTRFS_INODE_##name) { \ + if (!empty) \ + strcat(dst, "|"); \ + strcat(dst, #name); \ + empty = 0; \ + } \ +}) + +/* + * Caller should ensure sizeof(*ret) >= 102: all charactors plus '|' of + * BTRFS_INODE_* flags + */ static void inode_flags_to_str(u64 flags, char *ret) { int empty = 1; - if (flags & BTRFS_INODE_NODATASUM) { - empty = 0; - strcpy(ret, "NODATASUM"); - } - if (flags & BTRFS_INODE_NODATACOW) { - if (!empty) { - empty = 0; - strcat(ret, "|"); - } - strcat(ret, "NODATACOW"); - } - if (flags & BTRFS_INODE_READONLY) { - if (!empty) { - empty = 0; - strcat(ret, "|"); - } - strcat(ret, "READONLY"); - } + STRCAT_ONE_INODE_FLAG(flags, NODATASUM, empty, ret); + STRCAT_ONE_INODE_FLAG(flags, NODATACOW, empty, ret); + STRCAT_ONE_INODE_FLAG(flags, READONLY, empty, ret); + STRCAT_ONE_INODE_FLAG(flags, NOCOMPRESS, empty, ret); + STRCAT_ONE_INODE_FLAG(flags, PREALLOC, empty, ret); + STRCAT_ONE_INODE_FLAG(flags, SYNC, empty, ret); + STRCAT_ONE_INODE_FLAG(flags, IMMUTABLE, empty, ret); + STRCAT_ONE_INODE_FLAG(flags, APPEND, empty, ret); + STRCAT_ONE_INODE_FLAG(flags, NODUMP, empty, ret); + STRCAT_ONE_INODE_FLAG(flags, NOATIME, empty, ret); + STRCAT_ONE_INODE_FLAG(flags, DIRSYNC, empty, ret); + STRCAT_ONE_INODE_FLAG(flags, COMPRESS, empty, ret); if (empty) strcat(ret, "none"); } -void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l) +static void print_timespec(struct extent_buffer *eb, + struct btrfs_timespec *timespec, const char *prefix, + const char *suffix) +{ + struct tm tm; + u64 tmp_u64; + u32 tmp_u32; + time_t tmp_time; + char timestamp[256]; + + tmp_u64 = btrfs_timespec_sec(eb, timespec); + tmp_u32 = btrfs_timespec_nsec(eb, timespec); + tmp_time = tmp_u64; + localtime_r(&tmp_time, &tm); + strftime(timestamp, sizeof(timestamp), + "%Y-%m-%d %H:%M:%S", &tm); + printf("%s%llu.%u (%s)%s", prefix, (unsigned long long)tmp_u64, tmp_u32, + timestamp, suffix); +} + +static void print_inode_item(struct extent_buffer *eb, + struct btrfs_inode_item *ii) +{ + char flags_str[256]; + + memset(flags_str, 0, sizeof(flags_str)); + inode_flags_to_str(btrfs_inode_flags(eb, ii), flags_str); + printf("\t\tinode generation %llu transid %llu size %llu nbytes %llu\n" + "\t\tblock group %llu mode %o links %u uid %u gid %u rdev %llu\n" + "\t\tsequence %llu flags 0x%llx(%s)\n", + (unsigned long long)btrfs_inode_generation(eb, ii), + (unsigned long long)btrfs_inode_transid(eb, ii), + (unsigned long long)btrfs_inode_size(eb, ii), + (unsigned long long)btrfs_inode_nbytes(eb, ii), + (unsigned long long)btrfs_inode_block_group(eb,ii), + btrfs_inode_mode(eb, ii), + btrfs_inode_nlink(eb, ii), + btrfs_inode_uid(eb, ii), + btrfs_inode_gid(eb, ii), + (unsigned long long)btrfs_inode_rdev(eb,ii), + (unsigned long long)btrfs_inode_flags(eb,ii), + (unsigned long long)btrfs_inode_sequence(eb, ii), + flags_str); + print_timespec(eb, btrfs_inode_atime(ii), "\t\tatime ", "\n"); + print_timespec(eb, btrfs_inode_ctime(ii), "\t\tctime ", "\n"); + print_timespec(eb, btrfs_inode_mtime(ii), "\t\tmtime ", "\n"); + print_timespec(eb, btrfs_inode_otime(ii), "\t\totime ", "\n"); +} + +static void print_disk_balance_args(struct btrfs_disk_balance_args *ba) +{ + printf("\t\tprofiles %llu devid %llu target %llu flags %llu\n", + (unsigned long long)le64_to_cpu(ba->profiles), + (unsigned long long)le64_to_cpu(ba->devid), + (unsigned long long)le64_to_cpu(ba->target), + (unsigned long long)le64_to_cpu(ba->flags)); + printf("\t\tusage_min %u usage_max %u pstart %llu pend %llu\n", + le32_to_cpu(ba->usage_min), + le32_to_cpu(ba->usage_max), + (unsigned long long)le64_to_cpu(ba->pstart), + (unsigned long long)le64_to_cpu(ba->pend)); + printf("\t\tvstart %llu vend %llu limit_min %u limit_max %u\n", + (unsigned long long)le64_to_cpu(ba->vstart), + (unsigned long long)le64_to_cpu(ba->vend), + le32_to_cpu(ba->limit_min), + le32_to_cpu(ba->limit_max)); + printf("\t\tstripes_min %u stripes_max %u\n", + le32_to_cpu(ba->stripes_min), + le32_to_cpu(ba->stripes_max)); +} + +static void print_balance_item(struct extent_buffer *eb, + struct btrfs_balance_item *bi) +{ + printf("\t\tbalance status flags %llu\n", + btrfs_balance_item_flags(eb, bi)); + + printf("\t\tDATA\n"); + print_disk_balance_args(btrfs_balance_item_data(eb, bi)); + printf("\t\tMETADATA\n"); + print_disk_balance_args(btrfs_balance_item_meta(eb, bi)); + printf("\t\tSYSTEM\n"); + print_disk_balance_args(btrfs_balance_item_sys(eb, bi)); +} + +static void print_dev_stats(struct extent_buffer *eb, + struct btrfs_dev_stats_item *stats, u32 size) { int i; - char *str; + u32 known = BTRFS_DEV_STAT_VALUES_MAX * sizeof(__le64); + __le64 *values = btrfs_dev_stats_values(eb, stats); + + printf("\t\tdevice stats\n"); + printf("\t\twrite_errs %llu read_errs %llu flush_errs %llu corruption_errs %llu generation %llu\n", + (unsigned long long)le64_to_cpu(values[BTRFS_DEV_STAT_WRITE_ERRS]), + (unsigned long long)le64_to_cpu(values[BTRFS_DEV_STAT_READ_ERRS]), + (unsigned long long)le64_to_cpu(values[BTRFS_DEV_STAT_FLUSH_ERRS]), + (unsigned long long)le64_to_cpu(values[BTRFS_DEV_STAT_CORRUPTION_ERRS]), + (unsigned long long)le64_to_cpu(values[BTRFS_DEV_STAT_GENERATION_ERRS])); + + if (known < size) { + printf("\t\tunknown stats item bytes %u", size - known); + for (i = BTRFS_DEV_STAT_VALUES_MAX; i * sizeof(__le64) < size; i++) { + printf("\t\tunknown item %u offset %zu value %llu\n", + i, i * sizeof(__le64), + (unsigned long long)le64_to_cpu(values[i])); + } + } +} + +void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *eb) +{ struct btrfs_item *item; - struct btrfs_dir_item *di; - struct btrfs_inode_item *ii; - struct btrfs_file_extent_item *fi; - struct btrfs_block_group_item *bi; - struct btrfs_extent_data_ref *dref; - struct btrfs_shared_data_ref *sref; - struct btrfs_inode_ref *iref; - struct btrfs_inode_extref *iref2; - struct btrfs_dev_extent *dev_extent; struct btrfs_disk_key disk_key; - struct btrfs_block_group_item bg_item; - struct btrfs_free_space_info *free_info; - struct btrfs_dir_log_item *dlog; - struct btrfs_qgroup_info_item *qg_info; - struct btrfs_qgroup_limit_item *qg_limit; - struct btrfs_qgroup_status_item *qg_status; - u32 nr = btrfs_header_nritems(l); - u64 objectid; - u32 type; - char flags_str[32]; + u32 i; + u32 nr; + + nr = btrfs_header_nritems(eb); printf("leaf %llu items %d free space %d generation %llu owner %llu\n", - (unsigned long long)btrfs_header_bytenr(l), nr, - btrfs_leaf_free_space(root, l), - (unsigned long long)btrfs_header_generation(l), - (unsigned long long)btrfs_header_owner(l)); - print_uuids(l); + (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)); + print_uuids(eb); fflush(stdout); - for (i = 0 ; i < nr ; i++) { + + for (i = 0; i < nr; i++) { + u32 item_size; + void *ptr; + u64 objectid; + u32 type; + u64 offset; + char flags_str[256]; + char uuid_str[BTRFS_UUID_UNPARSED_SIZE]; + u8 uuid[BTRFS_UUID_SIZE]; + item = btrfs_item_nr(i); - btrfs_item_key(l, &disk_key, i); + item_size = btrfs_item_size(eb, item); + /* Untyped extraction of slot from btrfs_item_ptr */ + ptr = btrfs_item_ptr(eb, i, void*); + + btrfs_item_key(eb, &disk_key, i); objectid = btrfs_disk_key_objectid(&disk_key); type = btrfs_disk_key_type(&disk_key); + offset = btrfs_disk_key_offset(&disk_key); + printf("\titem %d ", i); btrfs_print_key(&disk_key); printf(" itemoff %d itemsize %d\n", - btrfs_item_offset(l, item), - btrfs_item_size(l, item)); + btrfs_item_offset(eb, item), + btrfs_item_size(eb, item)); if (type == 0 && objectid == BTRFS_FREE_SPACE_OBJECTID) - print_free_space_header(l, i); + print_free_space_header(eb, i); switch (type) { case BTRFS_INODE_ITEM_KEY: - memset(flags_str, 0, sizeof(flags_str)); - ii = btrfs_item_ptr(l, i, struct btrfs_inode_item); - inode_flags_to_str(btrfs_inode_flags(l, ii), flags_str); - printf("\t\tinode generation %llu transid %llu size %llu nbytes %llu\n" - "\t\tblock group %llu mode %o links %u uid %u gid %u\n" - "\t\trdev %llu flags 0x%llx(%s)\n", - (unsigned long long)btrfs_inode_generation(l, ii), - (unsigned long long)btrfs_inode_transid(l, ii), - (unsigned long long)btrfs_inode_size(l, ii), - (unsigned long long)btrfs_inode_nbytes(l, ii), - (unsigned long long)btrfs_inode_block_group(l,ii), - btrfs_inode_mode(l, ii), - btrfs_inode_nlink(l, ii), - btrfs_inode_uid(l, ii), - btrfs_inode_gid(l, ii), - (unsigned long long)btrfs_inode_rdev(l,ii), - (unsigned long long)btrfs_inode_flags(l,ii), - flags_str); + print_inode_item(eb, ptr); break; case BTRFS_INODE_REF_KEY: - iref = btrfs_item_ptr(l, i, struct btrfs_inode_ref); - print_inode_ref_item(l, item, iref); + print_inode_ref_item(eb, item_size, ptr); break; case BTRFS_INODE_EXTREF_KEY: - iref2 = btrfs_item_ptr(l, i, struct btrfs_inode_extref); - print_inode_extref_item(l, item, iref2); + print_inode_extref_item(eb, item_size, ptr); break; case BTRFS_DIR_ITEM_KEY: case BTRFS_DIR_INDEX_KEY: case BTRFS_XATTR_ITEM_KEY: - di = btrfs_item_ptr(l, i, struct btrfs_dir_item); - print_dir_item(l, item, di); + print_dir_item(eb, item_size, ptr); break; case BTRFS_DIR_LOG_INDEX_KEY: - case BTRFS_DIR_LOG_ITEM_KEY: - dlog = btrfs_item_ptr(l, i, struct btrfs_dir_log_item); + case BTRFS_DIR_LOG_ITEM_KEY: { + struct btrfs_dir_log_item *dlog; + + dlog = btrfs_item_ptr(eb, i, struct btrfs_dir_log_item); printf("\t\tdir log end %Lu\n", - (unsigned long long)btrfs_dir_log_end(l, dlog)); - break; + (unsigned long long)btrfs_dir_log_end(eb, dlog)); + break; + } case BTRFS_ORPHAN_ITEM_KEY: printf("\t\torphan item\n"); break; case BTRFS_ROOT_ITEM_KEY: - print_root(l, i); + print_root(eb, i); break; case BTRFS_ROOT_REF_KEY: - print_root_ref(l, i, "ref"); + print_root_ref(eb, i, "ref"); break; case BTRFS_ROOT_BACKREF_KEY: - print_root_ref(l, i, "backref"); + print_root_ref(eb, i, "backref"); break; case BTRFS_EXTENT_ITEM_KEY: - print_extent_item(l, i, 0); + print_extent_item(eb, i, 0); break; case BTRFS_METADATA_ITEM_KEY: - print_extent_item(l, i, 1); + print_extent_item(eb, i, 1); break; case BTRFS_TREE_BLOCK_REF_KEY: printf("\t\ttree block backref\n"); @@ -990,23 +1032,28 @@ void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l) case BTRFS_SHARED_BLOCK_REF_KEY: printf("\t\tshared block backref\n"); break; - case BTRFS_EXTENT_DATA_REF_KEY: - dref = btrfs_item_ptr(l, i, struct btrfs_extent_data_ref); + case BTRFS_EXTENT_DATA_REF_KEY: { + struct btrfs_extent_data_ref *dref; + + dref = btrfs_item_ptr(eb, i, struct btrfs_extent_data_ref); printf("\t\textent data backref root %llu " "objectid %llu offset %llu count %u\n", - (unsigned long long)btrfs_extent_data_ref_root(l, dref), - (unsigned long long)btrfs_extent_data_ref_objectid(l, dref), - (unsigned long long)btrfs_extent_data_ref_offset(l, dref), - btrfs_extent_data_ref_count(l, dref)); + (unsigned long long)btrfs_extent_data_ref_root(eb, dref), + (unsigned long long)btrfs_extent_data_ref_objectid(eb, dref), + (unsigned long long)btrfs_extent_data_ref_offset(eb, dref), + btrfs_extent_data_ref_count(eb, dref)); break; - case BTRFS_SHARED_DATA_REF_KEY: - sref = btrfs_item_ptr(l, i, struct btrfs_shared_data_ref); + } + case BTRFS_SHARED_DATA_REF_KEY: { + struct btrfs_shared_data_ref *sref; + sref = btrfs_item_ptr(eb, i, struct btrfs_shared_data_ref); printf("\t\tshared data backref count %u\n", - btrfs_shared_data_ref_count(l, sref)); + btrfs_shared_data_ref_count(eb, sref)); break; + } case BTRFS_EXTENT_REF_V0_KEY: #ifdef BTRFS_COMPAT_EXTENT_TREE_V0 - print_extent_ref_v0(l, i); + print_extent_ref_v0(eb, i); #else BUG(); #endif @@ -1018,14 +1065,12 @@ void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l) printf("\t\textent csum item\n"); break; case BTRFS_EXTENT_DATA_KEY: - fi = btrfs_item_ptr(l, i, - struct btrfs_file_extent_item); - print_file_extent_item(l, item, i, fi); + print_file_extent_item(eb, item, i, ptr); break; - case BTRFS_BLOCK_GROUP_ITEM_KEY: - bi = btrfs_item_ptr(l, i, - struct btrfs_block_group_item); - read_extent_buffer(l, &bg_item, (unsigned long)bi, + case BTRFS_BLOCK_GROUP_ITEM_KEY: { + struct btrfs_block_group_item bg_item; + + read_extent_buffer(eb, &bg_item, (unsigned long)ptr, sizeof(bg_item)); memset(flags_str, 0, sizeof(flags_str)); bg_flags_to_str(btrfs_block_group_flags(&bg_item), @@ -1035,12 +1080,16 @@ void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l) (unsigned long long)btrfs_block_group_chunk_objectid(&bg_item), flags_str); break; - case BTRFS_FREE_SPACE_INFO_KEY: - free_info = btrfs_item_ptr(l, i, struct btrfs_free_space_info); + } + case BTRFS_FREE_SPACE_INFO_KEY: { + struct btrfs_free_space_info *free_info; + + free_info = btrfs_item_ptr(eb, i, struct btrfs_free_space_info); printf("\t\tfree space info extent count %u flags %u\n", - (unsigned)btrfs_free_space_extent_count(l, free_info), - (unsigned)btrfs_free_space_flags(l, free_info)); + (unsigned)btrfs_free_space_extent_count(eb, free_info), + (unsigned)btrfs_free_space_flags(eb, free_info)); break; + } case BTRFS_FREE_SPACE_EXTENT_KEY: printf("\t\tfree space extent\n"); break; @@ -1048,93 +1097,134 @@ void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l) printf("\t\tfree space bitmap\n"); break; case BTRFS_CHUNK_ITEM_KEY: - print_chunk(l, btrfs_item_ptr(l, i, struct btrfs_chunk)); + print_chunk(eb, ptr); break; case BTRFS_DEV_ITEM_KEY: - print_dev_item(l, btrfs_item_ptr(l, i, - struct btrfs_dev_item)); + print_dev_item(eb, ptr); break; - case BTRFS_DEV_EXTENT_KEY: - dev_extent = btrfs_item_ptr(l, i, + case BTRFS_DEV_EXTENT_KEY: { + struct btrfs_dev_extent *dev_extent; + + dev_extent = btrfs_item_ptr(eb, i, struct btrfs_dev_extent); + read_extent_buffer(eb, uuid, + (unsigned long)btrfs_dev_extent_chunk_tree_uuid(dev_extent), + BTRFS_UUID_SIZE); + uuid_unparse(uuid, uuid_str); printf("\t\tdev extent chunk_tree %llu\n" - "\t\tchunk objectid %llu chunk offset %llu " - "length %llu\n", + "\t\tchunk_objectid %llu chunk_offset %llu " + "length %llu\n" + "\t\tchunk_tree_uuid %s\n", (unsigned long long) - btrfs_dev_extent_chunk_tree(l, dev_extent), + btrfs_dev_extent_chunk_tree(eb, dev_extent), (unsigned long long) - btrfs_dev_extent_chunk_objectid(l, dev_extent), + btrfs_dev_extent_chunk_objectid(eb, dev_extent), (unsigned long long) - btrfs_dev_extent_chunk_offset(l, dev_extent), + btrfs_dev_extent_chunk_offset(eb, dev_extent), (unsigned long long) - btrfs_dev_extent_length(l, dev_extent)); + btrfs_dev_extent_length(eb, dev_extent), + uuid_str); break; - case BTRFS_QGROUP_STATUS_KEY: - qg_status = btrfs_item_ptr(l, i, + } + case BTRFS_QGROUP_STATUS_KEY: { + struct btrfs_qgroup_status_item *qg_status; + + qg_status = btrfs_item_ptr(eb, i, struct btrfs_qgroup_status_item); memset(flags_str, 0, sizeof(flags_str)); - qgroup_flags_to_str(btrfs_qgroup_status_flags(l, qg_status), + qgroup_flags_to_str(btrfs_qgroup_status_flags(eb, qg_status), flags_str); printf("\t\tversion %llu generation %llu flags %s " "scan %lld\n", (unsigned long long) - btrfs_qgroup_status_version(l, qg_status), + btrfs_qgroup_status_version(eb, qg_status), (unsigned long long) - btrfs_qgroup_status_generation(l, qg_status), + btrfs_qgroup_status_generation(eb, qg_status), flags_str, (unsigned long long) - btrfs_qgroup_status_rescan(l, qg_status)); + btrfs_qgroup_status_rescan(eb, qg_status)); break; + } case BTRFS_QGROUP_RELATION_KEY: break; - case BTRFS_QGROUP_INFO_KEY: - qg_info = btrfs_item_ptr(l, i, + case BTRFS_QGROUP_INFO_KEY: { + struct btrfs_qgroup_info_item *qg_info; + + qg_info = btrfs_item_ptr(eb, i, struct btrfs_qgroup_info_item); printf("\t\tgeneration %llu\n" - "\t\treferenced %llu referenced compressed %llu\n" - "\t\texclusive %llu exclusive compressed %llu\n", + "\t\treferenced %llu referenced_compressed %llu\n" + "\t\texclusive %llu exclusive_compressed %llu\n", (unsigned long long) - btrfs_qgroup_info_generation(l, qg_info), + btrfs_qgroup_info_generation(eb, qg_info), (unsigned long long) - btrfs_qgroup_info_referenced(l, qg_info), + btrfs_qgroup_info_referenced(eb, qg_info), (unsigned long long) - btrfs_qgroup_info_referenced_compressed(l, + btrfs_qgroup_info_referenced_compressed(eb, qg_info), (unsigned long long) - btrfs_qgroup_info_exclusive(l, qg_info), + btrfs_qgroup_info_exclusive(eb, qg_info), (unsigned long long) - btrfs_qgroup_info_exclusive_compressed(l, + btrfs_qgroup_info_exclusive_compressed(eb, qg_info)); break; - case BTRFS_QGROUP_LIMIT_KEY: - qg_limit = btrfs_item_ptr(l, i, + } + case BTRFS_QGROUP_LIMIT_KEY: { + struct btrfs_qgroup_limit_item *qg_limit; + + qg_limit = btrfs_item_ptr(eb, i, struct btrfs_qgroup_limit_item); printf("\t\tflags %llx\n" - "\t\tmax referenced %lld max exclusive %lld\n" - "\t\trsv referenced %lld rsv exclusive %lld\n", + "\t\tmax_referenced %lld max_exclusive %lld\n" + "\t\trsv_referenced %lld rsv_exclusive %lld\n", (unsigned long long) - btrfs_qgroup_limit_flags(l, qg_limit), + btrfs_qgroup_limit_flags(eb, qg_limit), (long long) - btrfs_qgroup_limit_max_referenced(l, qg_limit), + btrfs_qgroup_limit_max_referenced(eb, qg_limit), (long long) - btrfs_qgroup_limit_max_exclusive(l, qg_limit), + btrfs_qgroup_limit_max_exclusive(eb, qg_limit), (long long) - btrfs_qgroup_limit_rsv_referenced(l, qg_limit), + btrfs_qgroup_limit_rsv_referenced(eb, qg_limit), (long long) - btrfs_qgroup_limit_rsv_exclusive(l, qg_limit)); + btrfs_qgroup_limit_rsv_exclusive(eb, qg_limit)); break; + } case BTRFS_UUID_KEY_SUBVOL: case BTRFS_UUID_KEY_RECEIVED_SUBVOL: - print_uuid_item(l, btrfs_item_ptr_offset(l, i), - btrfs_item_size_nr(l, i)); + print_uuid_item(eb, btrfs_item_ptr_offset(eb, i), + btrfs_item_size_nr(eb, i)); + break; + case BTRFS_STRING_ITEM_KEY: { + const char *str = eb->data + btrfs_item_ptr_offset(eb, i); + + printf("\t\titem data %.*s\n", item_size, str); break; - case BTRFS_STRING_ITEM_KEY: - /* dirty, but it's simple */ - str = l->data + btrfs_item_ptr_offset(l, i); - printf("\t\titem data %.*s\n", btrfs_item_size(l, item), str); + } + case BTRFS_PERSISTENT_ITEM_KEY: + printf("\t\tpersistent item objectid "); + print_objectid(stdout, objectid, BTRFS_PERSISTENT_ITEM_KEY); + printf(" offset %llu\n", (unsigned long long)offset); + switch (objectid) { + case BTRFS_DEV_STATS_OBJECTID: + print_dev_stats(eb, ptr, item_size); + break; + default: + printf("\t\tunknown persistent item objectid %llu\n", + objectid); + } break; - case BTRFS_DEV_STATS_KEY: - printf("\t\tdevice stats\n"); + case BTRFS_TEMPORARY_ITEM_KEY: + printf("\t\ttemporary item objectid "); + print_objectid(stdout, objectid, BTRFS_TEMPORARY_ITEM_KEY); + printf(" offset %llu\n", (unsigned long long)offset); + switch (objectid) { + case BTRFS_BALANCE_OBJECTID: + print_balance_item(eb, ptr); + break; + default: + printf("\t\tunknown temporary item objectid %llu\n", + objectid); + } break; }; fflush(stdout); @@ -1143,11 +1233,12 @@ void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l) void btrfs_print_tree(struct btrfs_root *root, struct extent_buffer *eb, int follow) { - int i; + u32 i; u32 nr; u32 size; struct btrfs_disk_key disk_key; struct btrfs_key key; + struct extent_buffer *next; if (!eb) return; @@ -1181,23 +1272,34 @@ void btrfs_print_tree(struct btrfs_root *root, struct extent_buffer *eb, int fol return; for (i = 0; i < nr; i++) { - struct extent_buffer *next = read_tree_block(root, - btrfs_node_blockptr(eb, i), - size, - btrfs_node_ptr_generation(eb, i)); + next = read_tree_block(root, btrfs_node_blockptr(eb, i), size, + btrfs_node_ptr_generation(eb, i)); if (!extent_buffer_uptodate(next)) { fprintf(stderr, "failed to read %llu in tree %llu\n", (unsigned long long)btrfs_node_blockptr(eb, i), (unsigned long long)btrfs_header_owner(eb)); continue; } - if (btrfs_is_leaf(next) && - btrfs_header_level(eb) != 1) - BUG(); - if (btrfs_header_level(next) != - btrfs_header_level(eb) - 1) - BUG(); + 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; + } btrfs_print_tree(root, next, 1); free_extent_buffer(next); } + + return; + +out: + free_extent_buffer(next); } diff --git a/qgroup-verify.c b/qgroup-verify.c index 66eb870a..ff46bc4c 100644 --- a/qgroup-verify.c +++ b/qgroup-verify.c @@ -330,9 +330,18 @@ static int find_parent_roots(struct ulist *roots, u64 parent) * For each unresolved root, we recurse */ ref = find_ref_bytenr(parent); + if (!ref) { + error("bytenr ref not found for parent %llu", + (unsigned long long)parent); + return -EIO; + } node = &ref->bytenr_node; - BUG_ON(ref == NULL); - BUG_ON(ref->bytenr != parent); + if (ref->bytenr != parent) { + error("found bytenr ref does not match parent: %llu != %llu", + (unsigned long long)ref->bytenr, + (unsigned long long)parent); + return -EIO; + } { /* @@ -341,9 +350,15 @@ static int find_parent_roots(struct ulist *roots, u64 parent) */ struct rb_node *prev_node = rb_prev(&ref->bytenr_node); struct ref *prev; + if (prev_node) { prev = rb_entry(prev_node, struct ref, bytenr_node); - BUG_ON(prev->bytenr == parent); + if (prev->bytenr == parent) { + error( + "unexpected: prev bytenr same as parent: %llu", + (unsigned long long)parent); + return -EIO; + } } } @@ -354,6 +369,11 @@ static int find_parent_roots(struct ulist *roots, u64 parent) if (ret < 0) goto out; } + } else if (ref->parent == ref->bytenr) { + /* + * Special loop case for tree reloc tree + */ + ref->root = BTRFS_TREE_RELOC_OBJECTID; } else { ret = find_parent_roots(roots, ref->parent); if (ret < 0) @@ -563,6 +583,8 @@ static u64 resolve_one_root(u64 bytenr) if (ref->root) return ref->root; + if (ref->parent == bytenr) + return BTRFS_TREE_RELOC_OBJECTID; return resolve_one_root(ref->parent); } @@ -733,6 +755,9 @@ static int add_refs_for_implied(struct btrfs_fs_info *info, u64 bytenr, struct btrfs_root *root; struct btrfs_key key; + /* Tree reloc tree doesn't contribute qgroup, skip it */ + if (root_id == BTRFS_TREE_RELOC_OBJECTID) + return 0; key.objectid = root_id; key.type = BTRFS_ROOT_ITEM_KEY; key.offset = (u64)-1; @@ -874,15 +899,14 @@ static int add_qgroup_relation(u64 memberid, u64 parentid) return 0; } -static void read_qgroup_status(struct btrfs_path *path, +static void read_qgroup_status(struct extent_buffer *eb, int slot, struct counts_tree *counts) { struct btrfs_qgroup_status_item *status_item; u64 flags; - status_item = btrfs_item_ptr(path->nodes[0], path->slots[0], - struct btrfs_qgroup_status_item); - flags = btrfs_qgroup_status_flags(path->nodes[0], status_item); + status_item = btrfs_item_ptr(eb, slot, struct btrfs_qgroup_status_item); + flags = btrfs_qgroup_status_flags(eb, status_item); /* * Since qgroup_inconsist/rescan_running is just one bit, * assign value directly won't work. @@ -946,7 +970,7 @@ loop: } if (key.type == BTRFS_QGROUP_STATUS_KEY) { - read_qgroup_status(&path, &counts); + read_qgroup_status(leaf, i, &counts); continue; } @@ -1455,29 +1479,24 @@ static int repair_qgroup_info(struct btrfs_fs_info *info, int ret; struct btrfs_root *root = info->quota_root; struct btrfs_trans_handle *trans; - struct btrfs_path *path; + struct btrfs_path path; struct btrfs_qgroup_info_item *info_item; struct btrfs_key key; printf("Repair qgroup %llu/%llu\n", btrfs_qgroup_level(count->qgroupid), btrfs_qgroup_subvid(count->qgroupid)); - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - trans = btrfs_start_transaction(root, 1); - if (IS_ERR(trans)) { - btrfs_free_path(path); + if (IS_ERR(trans)) return PTR_ERR(trans); - } + btrfs_init_path(&path); key.objectid = 0; key.type = BTRFS_QGROUP_INFO_KEY; key.offset = count->qgroupid; - ret = btrfs_search_slot(trans, root, &key, path, 0, 1); + ret = btrfs_search_slot(trans, root, &key, &path, 0, 1); if (ret) { - error("Could not find disk item for qgroup %llu/%llu.\n", + error("could not find disk item for qgroup %llu/%llu", btrfs_qgroup_level(count->qgroupid), btrfs_qgroup_subvid(count->qgroupid)); if (ret > 0) @@ -1485,27 +1504,27 @@ static int repair_qgroup_info(struct btrfs_fs_info *info, goto out; } - info_item = btrfs_item_ptr(path->nodes[0], path->slots[0], + info_item = btrfs_item_ptr(path.nodes[0], path.slots[0], struct btrfs_qgroup_info_item); - btrfs_set_qgroup_info_generation(path->nodes[0], info_item, + btrfs_set_qgroup_info_generation(path.nodes[0], info_item, trans->transid); - btrfs_set_qgroup_info_referenced(path->nodes[0], info_item, + btrfs_set_qgroup_info_referenced(path.nodes[0], info_item, count->info.referenced); - btrfs_set_qgroup_info_referenced_compressed(path->nodes[0], info_item, + btrfs_set_qgroup_info_referenced_compressed(path.nodes[0], info_item, count->info.referenced_compressed); - btrfs_set_qgroup_info_exclusive(path->nodes[0], info_item, + btrfs_set_qgroup_info_exclusive(path.nodes[0], info_item, count->info.exclusive); - btrfs_set_qgroup_info_exclusive_compressed(path->nodes[0], info_item, + btrfs_set_qgroup_info_exclusive_compressed(path.nodes[0], info_item, count->info.exclusive_compressed); - btrfs_mark_buffer_dirty(path->nodes[0]); + btrfs_mark_buffer_dirty(path.nodes[0]); out: btrfs_commit_transaction(trans, root); - btrfs_free_path(path); + btrfs_release_path(&path); return ret; } @@ -1515,53 +1534,48 @@ static int repair_qgroup_status(struct btrfs_fs_info *info) int ret; struct btrfs_root *root = info->quota_root; struct btrfs_trans_handle *trans; - struct btrfs_path *path; + struct btrfs_path path; struct btrfs_key key; struct btrfs_qgroup_status_item *status_item; printf("Repair qgroup status item\n"); - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - trans = btrfs_start_transaction(root, 1); - if (IS_ERR(trans)) { - btrfs_free_path(path); + if (IS_ERR(trans)) return PTR_ERR(trans); - } + btrfs_init_path(&path); key.objectid = 0; key.type = BTRFS_QGROUP_STATUS_KEY; key.offset = 0; - ret = btrfs_search_slot(trans, root, &key, path, 0, 1); + ret = btrfs_search_slot(trans, root, &key, &path, 0, 1); if (ret) { - error("Could not find qgroup status item\n"); + error("could not find qgroup status item"); if (ret > 0) ret = -ENOENT; goto out; } - status_item = btrfs_item_ptr(path->nodes[0], path->slots[0], + status_item = btrfs_item_ptr(path.nodes[0], path.slots[0], struct btrfs_qgroup_status_item); - btrfs_set_qgroup_status_flags(path->nodes[0], status_item, + btrfs_set_qgroup_status_flags(path.nodes[0], status_item, BTRFS_QGROUP_STATUS_FLAG_ON); - btrfs_set_qgroup_status_rescan(path->nodes[0], status_item, 0); - btrfs_set_qgroup_status_generation(path->nodes[0], status_item, + btrfs_set_qgroup_status_rescan(path.nodes[0], status_item, 0); + btrfs_set_qgroup_status_generation(path.nodes[0], status_item, trans->transid); - btrfs_mark_buffer_dirty(path->nodes[0]); + btrfs_mark_buffer_dirty(path.nodes[0]); out: btrfs_commit_transaction(trans, root); - btrfs_free_path(path); + btrfs_release_path(&path); return ret; } int repair_qgroups(struct btrfs_fs_info *info, int *repaired) { - int ret; + int ret = 0; struct qgroup_count *count, *tmpcount; *repaired = 0; @@ -148,7 +148,7 @@ void btrfs_qgroup_setup_print_column(enum btrfs_qgroup_column_enum column) { int i; - BUG_ON(column < 0 || column > BTRFS_QGROUP_ALL); + ASSERT(0 <= column && column <= BTRFS_QGROUP_ALL); if (column < BTRFS_QGROUP_ALL) { btrfs_qgroup_columns[column].need_print = 1; @@ -213,11 +213,12 @@ static void print_qgroup_column_add_blank(enum btrfs_qgroup_column_enum column, static void print_qgroup_column(struct btrfs_qgroup *qgroup, enum btrfs_qgroup_column_enum column) { - BUG_ON(column >= BTRFS_QGROUP_ALL || column < 0); int len; int unit_mode = btrfs_qgroup_columns[column].unit_mode; int max_len = btrfs_qgroup_columns[column].max_len; + ASSERT(0 <= column && column < BTRFS_QGROUP_ALL); + switch (column) { case BTRFS_QGROUP_QGROUPID: @@ -438,7 +439,7 @@ struct btrfs_qgroup_comparer_set *btrfs_qgroup_alloc_comparer_set(void) sizeof(struct btrfs_qgroup_comparer); set = calloc(1, size); if (!set) { - fprintf(stderr, "memory allocation failed\n"); + error("memory allocation failed"); exit(1); } @@ -447,11 +448,6 @@ struct btrfs_qgroup_comparer_set *btrfs_qgroup_alloc_comparer_set(void) return set; } -void btrfs_qgroup_free_comparer_set(struct btrfs_qgroup_comparer_set *comp_set) -{ - free(comp_set); -} - int btrfs_qgroup_setup_comparer(struct btrfs_qgroup_comparer_set **comp_set, enum btrfs_qgroup_comp_enum comparer, int is_descending) @@ -459,9 +455,9 @@ int btrfs_qgroup_setup_comparer(struct btrfs_qgroup_comparer_set **comp_set, struct btrfs_qgroup_comparer_set *set = *comp_set; int size; - BUG_ON(!set); - BUG_ON(comparer >= BTRFS_QGROUP_COMP_MAX); - BUG_ON(set->ncomps > set->total); + ASSERT(set != NULL); + ASSERT(comparer < BTRFS_QGROUP_COMP_MAX); + ASSERT(set->ncomps <= set->total); if (set->ncomps == set->total) { void *tmp; @@ -472,7 +468,7 @@ int btrfs_qgroup_setup_comparer(struct btrfs_qgroup_comparer_set **comp_set, tmp = set; set = realloc(set, size); if (!set) { - fprintf(stderr, "memory allocation failed\n"); + error("memory allocation failed"); free(tmp); exit(1); } @@ -484,7 +480,7 @@ int btrfs_qgroup_setup_comparer(struct btrfs_qgroup_comparer_set **comp_set, *comp_set = set; } - BUG_ON(set->comps[set->ncomps].comp_func); + ASSERT(set->comps[set->ncomps].comp_func == NULL); set->comps[set->ncomps].comp_func = all_comp_funcs[comparer]; set->comps[set->ncomps].is_descending = is_descending; @@ -616,7 +612,7 @@ static int update_qgroup(struct qgroup_lookup *qgroup_lookup, u64 qgroupid, if (pa && child) { list = malloc(sizeof(*list)); if (!list) { - fprintf(stderr, "memory allocation failed\n"); + error("memory allocation failed"); exit(1); } list->qgroup = pa; @@ -645,7 +641,7 @@ static int add_qgroup(struct qgroup_lookup *qgroup_lookup, u64 qgroupid, bq = calloc(1, sizeof(*bq)); if (!bq) { - printf("memory allocation failed\n"); + error("memory allocation failed"); exit(1); } if (qgroupid) { @@ -674,7 +670,7 @@ static int add_qgroup(struct qgroup_lookup *qgroup_lookup, u64 qgroupid, if (parent && child) { list = malloc(sizeof(*list)); if (!list) { - fprintf(stderr, "memory allocation failed\n"); + error("memory allocation failed"); exit(1); } list->qgroup = parent; @@ -684,8 +680,8 @@ static int add_qgroup(struct qgroup_lookup *qgroup_lookup, u64 qgroupid, } ret = qgroup_tree_insert(qgroup_lookup, bq); if (ret) { - printf("failed to insert tree %llu\n", - bq->qgroupid); + error("failed to insert %llu into tree: %s", + (unsigned long long)bq->qgroupid, strerror(-ret)); exit(1); } return ret; @@ -813,7 +809,7 @@ struct btrfs_qgroup_filter_set *btrfs_qgroup_alloc_filter_set(void) sizeof(struct btrfs_qgroup_filter); set = calloc(1, size); if (!set) { - fprintf(stderr, "memory allocation failed\n"); + error("memory allocation failed"); exit(1); } set->total = BTRFS_QGROUP_NFILTERS_INCREASE; @@ -821,20 +817,15 @@ struct btrfs_qgroup_filter_set *btrfs_qgroup_alloc_filter_set(void) return set; } -void btrfs_qgroup_free_filter_set(struct btrfs_qgroup_filter_set *filter_set) -{ - free(filter_set); -} - int btrfs_qgroup_setup_filter(struct btrfs_qgroup_filter_set **filter_set, enum btrfs_qgroup_filter_enum filter, u64 data) { struct btrfs_qgroup_filter_set *set = *filter_set; int size; - BUG_ON(!set); - BUG_ON(filter >= BTRFS_QGROUP_FILTER_MAX); - BUG_ON(set->nfilters > set->total); + ASSERT(set != NULL); + ASSERT(filter < BTRFS_QGROUP_FILTER_MAX); + ASSERT(set->nfilters <= set->total); if (set->nfilters == set->total) { void *tmp; @@ -845,7 +836,7 @@ int btrfs_qgroup_setup_filter(struct btrfs_qgroup_filter_set **filter_set, tmp = set; set = realloc(set, size); if (!set) { - fprintf(stderr, "memory allocation failed\n"); + error("memory allocation failed"); free(tmp); exit(1); } @@ -855,7 +846,8 @@ int btrfs_qgroup_setup_filter(struct btrfs_qgroup_filter_set **filter_set, set->total += BTRFS_QGROUP_NFILTERS_INCREASE; *filter_set = set; } - BUG_ON(set->filters[set->nfilters].filter_func); + + ASSERT(set->filters[set->nfilters].filter_func == NULL); set->filters[set->nfilters].filter_func = all_filter_funcs[filter]; set->filters[set->nfilters].data = data; set->nfilters++; @@ -926,12 +918,13 @@ static int sort_tree_insert(struct qgroup_lookup *sort_tree, static void __update_columns_max_len(struct btrfs_qgroup *bq, enum btrfs_qgroup_column_enum column) { - BUG_ON(column >= BTRFS_QGROUP_ALL || column < 0); struct btrfs_qgroup_list *list = NULL; char tmp[100]; int len; unsigned unit_mode = btrfs_qgroup_columns[column].unit_mode; + ASSERT(0 <= column && column < BTRFS_QGROUP_ALL); + switch (column) { case BTRFS_Q |