btrfs-progs
btrfs-progs copied to clipboard
Btrfs filesystem show JSON format support
Introduce JSON format support for btrfs subvolume show, there are two ways this information can be obtained either via an ioctl or via reading the block device. So I have taken the liberty of merging the print paths together in a re-usable function before adding JSON support.
I've chosen to format the JSON as I would have expected, an Object with "metadata" and the list of devices as part of that under device-list. See for example a single device volume:
{
"__header": {
"version": "1"
},
"filesystem-list": [
{
"label": "fedora",
"uuid": "cece4dd8-6168-4c88-a4a8-f7c51ed4f82b",
"total_devices": 1,
"used": 3334180864,
"device-list": [
{
"devid": 1,
"size": 0,
"used": 0,
"path": "/dev/sda5"
}
]
}
]
}
There is one open issue, when you have an unmounted btrfs volume with multiple devices where one is missing it won't show up the json output:
{
"__header": {
"version": "1"
},
WARNING: warning, device 2 is missing
"filesystem-list": [
{
"label": "raid1",
"uuid": "698b3250-9424-46c4-af61-372cc5468780",
"total_devices": 2,
"used": 638779392,
"device-list": [
{
"devid": 1,
"size": 0,
"used": 0,
"path": "/dev/sdb"
}
]
}
]
}
I could add a missing_devices property to the btrfs filesystem information but then that is inconsistent with the mounted missing device output (where it is a missing property in the device object).
Is it possible to show what device is missing for the unmounted case? So btrfs subvolume show would be consistent either way? (Seems device id is known, not sure about the path?).
Is it possible to show what device is missing for the unmounted case? So btrfs subvolume show would be consistent either way?
Generally no, but there are a few exceptions. The ioctl and block methods build the device list from sources with different information availability.
The ioctl (mounted) method can simply read the chunk tree from the kernel and get all information about all devices except for path, which is filled in by searching /dev and matching up devuuids (there's some udev in between, but /dev + brute-force search is the ultimate source of the information). This can build a complete list of missing device IDs, UUIDs, and sizes (which are stored in the metadata) but not paths (which are missing by definition) or any data that requires the path as an input (such as device slack or rotational status). Consistency is expected because btrfs wouldn't mount the filesystem otherwise (the information could change while fi show is running, so inconsistency is still possible).
The block device method reads the superblock of every device available in /dev, which tells it how many devices to expect per filesystem and the devid and devuuid of every device that is present. No information is available about devices that are missing. The number of missing devices is the difference between the number of devices found and the number of devices expected. If the number of devices expected is lower than the largest device ID in the filesystem, then it's not possible to tell which device IDs are missing from the available information. If number of devices expected is equal to the largest device ID, then the missing devices are any devid numbers between 1 and the number of devices expected that are not found during the superblock search. If there's an existing device ID that is higher than num_devices then you know you don't know what the missing device IDs are. If the highest device ID found is smaller than num_devices but some devices are missing, then you don't have enough information to know whether some of the missing devices have higher device IDs than num_devices. Inconsistent data is also possible, e.g. a device is removed from a filesystem while offline, then reappears with a superblock and metadata that results in num_devices less than the number of devices present, or multiple values of num_devices for a single filesystem, or devices with the same fs uuid and dev id but different devuuid.
For both methods to behave like the ioctl case, you'd have to read the device tree from the metadata to get the device list (i.e. emulate the ioctl in userspace). If too many devices are missing or there's inconsistent superblocks present, reading the tree won't be possible, and the block device method is the only option.
Thanks for the detailed information @Zygo, do you consider not having the missing devices in the output a blocker for merging this PR?
The JSON output should be consistent with the legacy text output in either mounted or unmounted cases, and the missing devices reports are important, so I think we do need to figure out how to represent them in JSON.
The number of devices is always known (every device's superblock has a num_devices field), so we know the minimum number of entries to expect in the device array. In the unmounted case, after enumerating all the devices that are present, the device array can be filled with empty device nodes (no devid, path, used, or size because we don't have that information) until it contains total_devices nodes. But then there's nothing to put in those elements at all, since we know nothing about those devices. Maybe put a "missing" bool there, so it looks like the mounted case? So for three devices with one offline, we could get:
{
"__header": {
"version": "1"
},
WARNING: warning, device 2 is missing
"filesystem-list": [
{
"label": "raid1",
"uuid": "698b3250-9424-46c4-af61-372cc5468780",
"total_devices": 3,
"used": 638779392,
"device-list": [
{
"devid": 5,
"size": 0,
"used": 0,
"path": "/dev/sdb",
"missing": false
},
{
"missing": true,
},
{
"missing": true,
}
]
}
]
}
Missing devices never have paths--a device with a path is not missing. The converse isn't always true--if a /dev node is deleted after a filesystem is mounted, then there could be no path to a device that is nonetheless online and in use. So a separate per-device "missing" bool field makes sense to distinguish between devices that are online but where path can't be determined, and devices that are really missing. (Note: when I tested this with btrfs fi show just now, it failed...that's out of scope for this PR, but there are more problems to solve here than output formatting)
Missing devices might not have IDs. You might know that there are 3 devices, but if only 1 device is online, and you can't read the device tree, you don't know what any of the missing device IDs are.
To add another wrinkle, device replace adds an additional device with devid 0, without changing num_devices in the superblock (but apparently btrfs fi show does know about the extra device). So you might have more devices in the device array than there are in total_devices. I would not recommend users compare total_devices with the size of the device-list array because of corner cases like that one.
devid shouldn't be an integer if the device is missing and the devid is unknown--even negative numbers are off limits, as they may conflict with some future btrfs extension. For that matter...same with "size" and "used", they should only be present when known. 0 is a valid value for both: size is set to 0 during device delete, and a device could be empty.
I could add a
missing_devicesproperty to the btrfs filesystem information but then that is inconsistent with the mounted missing device output (where it is a missing property in the device object).
That might be useful to have as a separate filesystem-level field in either mounted or unmounted cases. It could be a count of the number of devices that are known to be missing: in the mounted case, the number of devices where the missing attribute is true, and in the unmounted case, the difference between the number of devices expected vs found.