Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Btrfs filesystem show JSON format support #761

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

jelly
Copy link

@jelly jelly commented Mar 21, 2024

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?).

jelly added 3 commits June 5, 2024 09:18
Introduce helper functions for printing the filesystem data and list of
devices. This prepares the both printing code paths of filesystem show
to support json output.

Signed-off-by: Jelle van der Waa <[email protected]>
To support JSON formatted output for `filesystem show` stdout can't
contain warnings, the warning macro prints to stderr.
Implements JSON-formatted output for the `filesystem show` command using
the `--format json` global option. Devices are in a `device-list` array,
optionally showing if a device is missing.

Signed-off-by: Jelle van der Waa <[email protected]>
@jelly jelly force-pushed the btrfs-filesystem-show-json branch from a42f5cd to 75d699b Compare June 5, 2024 07:18
@Zygo
Copy link

Zygo commented Jun 5, 2024

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.

@jelly
Copy link
Author

jelly commented Jul 29, 2024

Thanks for the detailed information @Zygo, do you consider not having the missing devices in the output a blocker for merging this PR?

@Zygo
Copy link

Zygo commented Jul 30, 2024

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_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).

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants