/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _BCACHEFS_IOCTL_H
#define _BCACHEFS_IOCTL_H

#include <linux/uuid.h>
#include <asm/ioctl.h>
#include "bcachefs_format.h"
#include "btree/bkey_types.h"

/*
 * Flags common to multiple ioctls:
 */
#define BCH_FORCE_IF_DATA_LOST		(1 << 0)
#define BCH_FORCE_IF_METADATA_LOST	(1 << 1)
#define BCH_FORCE_IF_DATA_DEGRADED	(1 << 2)
#define BCH_FORCE_IF_METADATA_DEGRADED	(1 << 3)

#define BCH_FORCE_IF_LOST			\
	(BCH_FORCE_IF_DATA_LOST|		\
	 BCH_FORCE_IF_METADATA_LOST)
#define BCH_FORCE_IF_DEGRADED			\
	(BCH_FORCE_IF_DATA_DEGRADED|		\
	 BCH_FORCE_IF_METADATA_DEGRADED)

/*
 * If cleared, ioctl that refer to a device pass it as a pointer to a pathname
 * (e.g. /dev/sda1); if set, the dev field is the device's index within the
 * filesystem:
 */
#define BCH_BY_INDEX			(1 << 4)

/*
 * For BCH_IOCTL_READ_SUPER: get superblock of a specific device, not filesystem
 * wide superblock:
 */
#define BCH_READ_DEV			(1 << 5)

/* filesystem ioctls: */

#define BCH_IOCTL_QUERY_UUID	_IOR(0xbc,	1,  struct bch_ioctl_query_uuid)

#define BCH_IOCTL_DISK_ADD		_IOW(0xbc,	4,  struct bch_ioctl_disk)
#define BCH_IOCTL_DISK_ADD_v2		_IOW(0xbc,	23, struct bch_ioctl_disk_v2)
#define BCH_IOCTL_DISK_REMOVE		_IOW(0xbc,	5,  struct bch_ioctl_disk)
#define BCH_IOCTL_DISK_REMOVE_v2	_IOW(0xbc,	24, struct bch_ioctl_disk_v2)
#define BCH_IOCTL_DISK_ONLINE		_IOW(0xbc,	6,  struct bch_ioctl_disk)
#define BCH_IOCTL_DISK_ONLINE_v2	_IOW(0xbc,	25, struct bch_ioctl_disk_v2)
#define BCH_IOCTL_DISK_OFFLINE		_IOW(0xbc,	7,  struct bch_ioctl_disk)
#define BCH_IOCTL_DISK_OFFLINE_v2	_IOW(0xbc,	26, struct bch_ioctl_disk_v2)
#define BCH_IOCTL_DISK_SET_STATE	_IOW(0xbc,	8,  struct bch_ioctl_disk_set_state)
#define BCH_IOCTL_DISK_SET_STATE_v2	_IOW(0xbc,	22, struct bch_ioctl_disk_set_state_v2)
#define BCH_IOCTL_DATA			_IOW(0xbc,	10, struct bch_ioctl_data)
#define BCH_IOCTL_FS_USAGE		_IOWR(0xbc,	11, struct bch_ioctl_fs_usage)
#define BCH_IOCTL_DEV_USAGE		_IOWR(0xbc,	11, struct bch_ioctl_dev_usage)
#define BCH_IOCTL_READ_SUPER		_IOW(0xbc,	12, struct bch_ioctl_read_super)
#define BCH_IOCTL_DISK_GET_IDX		_IOW(0xbc,	13, struct bch_ioctl_disk_get_idx)
#define BCH_IOCTL_DISK_RESIZE		_IOW(0xbc,	14, struct bch_ioctl_disk_resize)
#define BCH_IOCTL_DISK_RESIZE_v2	_IOW(0xbc,	27, struct bch_ioctl_disk_resize_v2)
#define BCH_IOCTL_DISK_RESIZE_JOURNAL	_IOW(0xbc,	15, struct bch_ioctl_disk_resize_journal)
#define BCH_IOCTL_DISK_RESIZE_JOURNAL_v2 _IOW(0xbc,	28, struct bch_ioctl_disk_resize_journal_v2)

#define BCH_IOCTL_SUBVOLUME_CREATE	_IOW(0xbc,	16, struct bch_ioctl_subvolume)
#define BCH_IOCTL_SUBVOLUME_CREATE_v2	_IOW(0xbc,	29, struct bch_ioctl_subvolume_v2)
#define BCH_IOCTL_SUBVOLUME_DESTROY	_IOW(0xbc,	17, struct bch_ioctl_subvolume)
#define BCH_IOCTL_SUBVOLUME_DESTROY_v2	_IOW(0xbc,	30, struct bch_ioctl_subvolume_v2)

#define BCH_IOCTL_DEV_USAGE_V2		_IOWR(0xbc,	18, struct bch_ioctl_dev_usage_v2)

#define BCH_IOCTL_FSCK_OFFLINE		_IOW(0xbc,	19, struct bch_ioctl_fsck_offline)
#define BCH_IOCTL_FSCK_ONLINE		_IOW(0xbc,	20, struct bch_ioctl_fsck_online)
#define BCH_IOCTL_QUERY_ACCOUNTING	_IOW(0xbc,	21, struct bch_ioctl_query_accounting)
#define BCH_IOCTL_QUERY_COUNTERS	_IOW(0xbc,	21, struct bch_ioctl_query_counters)
#define BCH_IOCTL_SUBVOLUME_LIST	_IOWR(0xbc,	31, struct bch_ioctl_subvol_readdir)
#define BCH_IOCTL_SUBVOLUME_TO_PATH	_IOWR(0xbc,	32, struct bch_ioctl_subvol_to_path)
#define BCH_IOCTL_SNAPSHOT_TREE		_IOWR(0xbc,	33, struct bch_ioctl_snapshot_tree_query)
#define BCH_IOCTL_QUERY_BTREE_KEYS	_IOWR(0xbc,	34, struct bch_ioctl_query_btree_keys)
#define BCH_IOCTL_SNAPSHOT_TREE_v2	_IOWR(0xbc,	35, struct bch_ioctl_snapshot_tree_query_v2)
#define BCH_IOCTL_RECOVERY_STATUS	_IOR(0xbc,	36, struct bch_ioctl_recovery_status)

/* ioctl below act on a particular file, not the filesystem as a whole: */

#define BCHFS_IOC_REINHERIT_ATTRS	_IOR(0xbc, 64, const char __user *)
#define BCHFS_IOC_SET_REFLINK_P_MAY_UPDATE_OPTS	_IO(0xbc, 65)
#define BCHFS_IOC_PROPAGATE_REFLINK_P_OPTS	_IO(0xbc, 66)
#define BCHFS_IOC_PREAD_RAW		_IOWR(0xbc, 67, struct bch_ioctl_pread_raw)
#define BCHFS_IOC_UNPOISON		_IOW(0xbc, 68, struct bch_ioctl_unpoison)
#define BCHFS_IOC_GET_DAMAGE		_IOWR(0xbc, 70, struct bch_ioctl_get_damage)
#define BCHFS_IOC_READDIR_FLAGS		_IOWR(0xbc, 69, struct bch_ioctl_readdir_flags)

/*
 * BCHFS_IOC_CLEAR_DAMAGE: clear the file's damage record, in the calling
 * subvolume's view - snapshots keep theirs (on disk the clear is a
 * whiteout when an older version still needs the record). Requires
 * ownership or CAP_FOWNER, like chattr.
 */
#define BCHFS_IOC_CLEAR_DAMAGE		_IO(0xbc, 71)

/*
 * BCHFS_IOC_GET_DAMAGE: the accumulated damage record for this file - the
 * union of damage recorded against its inode in the file's snapshot and
 * all ancestor snapshot versions, since damage done to an ancestor version
 * is damage to the file seen here.
 *
 * @nr_entries	- in: capacity of @entries; out: number present. A result
 *		  exceeding the capacity reports the true count - retry
 *		  with more room.
 * @entries	- sorted by error id; the same records the errors
 *		  superblock section keeps (bch_sb_field_error_entry_v2):
 *		  the id, a saturating occurrence count and the times of
 *		  first and last occurrence, unpacked with
 *		  BCH_SB_ERROR_ENTRY_V2_ID/NR/FIRST/LAST
 */
struct bch_ioctl_get_damage {
	__u32			nr_entries;
	__u32			pad;
	bch_sb_field_error_entry_v2 entries[];
};

/*
 * BCHFS_IOC_READDIR_FLAGS: readdir with filters, on the directory the
 * ioctl is called on.
 *
 * recursive: entries from the whole subtree, names become paths relative
 * to the fd's directory. The filters pick the iteration: damaged
 * walks the damage btree (cost proportional to recorded damage, not tree
 * size), subvolumes_only walks the subvolume tree, an unfiltered
 * recursive listing is an honest tree walk.
 *
 * damaged: only entries whose inode has recorded damage, in its
 * snapshot version or an ancestor.
 *
 * Permissions are those of readdir: the caller learns nothing beyond the
 * directory they opened.
 *
 * @flags	- BCH_READDIR_*
 * @pos		- opaque resume cursor: zero to start; copied back out
 *		  past the last entry returned. Iterate until @used == 0.
 * @buf_size,
 * @buf		- userspace buffer, filled with struct
 *		  bch_ioctl_readdir_entry records
 * @used	- out: bytes of @buf filled
 */
#define BCH_READDIR_recursive		(1U << 0)
#define BCH_READDIR_damaged	(1U << 2)
#define BCH_READDIR_subvolumes_only	(1U << 1)

struct bch_ioctl_readdir_flags {
	__u64			pos[2];
	__u64			buf;
	__u32			buf_size;
	__u32			flags;
	__u32			used;
	__u32			pad;
};

/*
 * One entry: the NUL-terminated name - a relative path, under recursive -
 * follows the fixed header; entries are padded to 8-byte alignment.
 * Deliberately knows nothing about the filters that selected it: damage
 * details come from BCHFS_IOC_GET_DAMAGE on the file itself.
 */
struct bch_ioctl_readdir_entry {
	__u64			inum;
	__u8			d_type;
	__u8			pad;
	__u16			name_len;	/* including the NUL */
	__u8			name[];
};

struct bch_ioctl_err_msg {
	__u64			msg_ptr;
	__u32			msg_len;
	__u32			pad;
};

/*
 * BCH_IOCTL_QUERY_UUID: get filesystem UUID
 *
 * Returns user visible UUID, not internal UUID (which may not ever be changed);
 * the filesystem's sysfs directory may be found under /sys/fs/bcachefs with
 * this UUID.
 */
struct bch_ioctl_query_uuid {
	__uuid_t		uuid;
};

/*
 * BCH_IOCTL_DISK_ADD: add a new device to an existing filesystem
 *
 * The specified device must not be open or in use. On success, the new device
 * will be an online member of the filesystem just like any other member.
 *
 * The device must first be prepared by userspace by formatting with a bcachefs
 * superblock, which is only used for passing in superblock options/parameters
 * for that device (in struct bch_member). The new device's superblock should
 * not claim to be a member of any existing filesystem - UUIDs on it will be
 * ignored.
 */

/*
 * BCH_IOCTL_DISK_REMOVE: permanently remove a member device from a filesystem
 *
 * Any data present on @dev will be permanently deleted, and @dev will be
 * removed from its slot in the filesystem's list of member devices. The device
 * may be either offline or offline.
 *
 * Will fail removing @dev would leave us with insufficient read write devices
 * or degraded/unavailable data, unless the approprate BCH_FORCE_IF_* flags are
 * set.
 */

/*
 * BCH_IOCTL_DISK_ONLINE: given a disk that is already a member of a filesystem
 * but is not open (e.g. because we started in degraded mode), bring it online
 *
 * all existing data on @dev will be available once the device is online,
 * exactly as if @dev was present when the filesystem was first mounted
 */

/*
 * BCH_IOCTL_DISK_OFFLINE: offline a disk, causing the kernel to close that
 * block device, without removing it from the filesystem (so it can be brought
 * back online later)
 *
 * Data present on @dev will be unavailable while @dev is offline (unless
 * replicated), but will still be intact and untouched if @dev is brought back
 * online
 *
 * Will fail (similarly to BCH_IOCTL_DISK_SET_STATE) if offlining @dev would
 * leave us with insufficient read write devices or degraded/unavailable data,
 * unless the approprate BCH_FORCE_IF_* flags are set.
 */

struct bch_ioctl_disk {
	__u32			flags;
	__u32			pad;
	__u64			dev;
};

struct bch_ioctl_disk_v2 {
	__u32				flags;
	__u32				pad;
	__u64				dev;
	struct bch_ioctl_err_msg	err;
};

/*
 * BCH_IOCTL_DISK_SET_STATE: modify state of a member device of a filesystem
 *
 * @new_state		- one of the bch_member_state states (rw, ro, failed,
 *			  spare)
 *
 * Will refuse to change member state if we would then have insufficient devices
 * to write to, or if it would result in degraded data (when @new_state is
 * failed or spare) unless the appropriate BCH_FORCE_IF_* flags are set.
 */
struct bch_ioctl_disk_set_state {
	__u32			flags;
	__u8			new_state;
	__u8			pad[3];
	__u64			dev;
};

struct bch_ioctl_disk_set_state_v2 {
	__u32				flags;
	__u8				new_state;
	__u8				pad[3];
	__u64				dev;
	struct bch_ioctl_err_msg	err;
};

#define BCH_DATA_OPS()			\
	x(scrub,		0)	\
	x(rereplicate,		1)	\
	x(migrate,		2)	\
	x(rewrite_old_nodes,	3)	\
	x(drop_extra_replicas,	4)

enum bch_data_ops {
#define x(t, n) BCH_DATA_OP_##t = n,
	BCH_DATA_OPS()
#undef x
	BCH_DATA_OP_NR
};

/*
 * BCH_IOCTL_DATA: operations that walk and manipulate filesystem data (e.g.
 * scrub, rereplicate, migrate).
 *
 * This ioctl kicks off a job in the background, and returns a file descriptor.
 * Reading from the file descriptor returns a struct bch_ioctl_data_event,
 * indicating current progress, and closing the file descriptor will stop the
 * job. The file descriptor is O_CLOEXEC.
 */
struct bch_ioctl_data {
	__u16			op;
	__u8			start_btree;
	__u8			end_btree;
	__u32			flags;

	struct bpos		start_pos;
	struct bpos		end_pos;

	union {
	struct {
		__u32		dev;
		__u32		data_types;
	}			scrub;
	struct {
		__u32		dev;
		__u32		pad;
	}			migrate;
	struct {
		__u64		pad[8];
	};
	};
} __packed __aligned(8);

enum bch_data_event {
	BCH_DATA_EVENT_PROGRESS	= 0,
	/* XXX: add an event for reporting errors */
	BCH_DATA_EVENT_NR	= 1,
};

enum data_progress_data_type_special {
	DATA_PROGRESS_DATA_TYPE_phys	= 254,
	DATA_PROGRESS_DATA_TYPE_done	= 255,
};

struct bch_ioctl_data_progress {
	__u8			data_type;
	__u8			btree_id;
	__u8			pad[2];
	struct bpos		pos;

	__u64			sectors_done;
	__u64			sectors_total;
	__u64			sectors_error_corrected;
	__u64			sectors_error_uncorrected;
} __packed __aligned(8);

enum bch_ioctl_data_event_ret {
	BCH_IOCTL_DATA_EVENT_RET_done		= 1,
	BCH_IOCTL_DATA_EVENT_RET_device_offline	= 2,
};

struct bch_ioctl_data_event {
	__u8			type;
	__u8			ret;
	__u8			pad[6];
	union {
	struct bch_ioctl_data_progress p;
	__u64			pad2[15];
	};
} __aligned(8);

struct bch_replicas_usage {
	__u64			sectors;
	struct bch_replicas_entry_v1 r;
} __packed;

static inline unsigned replicas_usage_bytes(struct bch_replicas_usage *u)
{
	return offsetof(struct bch_replicas_usage, r) + replicas_entry_bytes(&u->r);
}

static inline struct bch_replicas_usage *
replicas_usage_next(struct bch_replicas_usage *u)
{
	return (void *) u + replicas_usage_bytes(u);
}

/* Obsolete */
/*
 * BCH_IOCTL_FS_USAGE: query filesystem disk space usage
 *
 * Returns disk space usage broken out by data type, number of replicas, and
 * by component device
 *
 * @replica_entries_bytes - size, in bytes, allocated for replica usage entries
 *
 * On success, @replica_entries_bytes will be changed to indicate the number of
 * bytes actually used.
 *
 * Returns -ERANGE if @replica_entries_bytes was too small
 */
struct bch_ioctl_fs_usage {
	__u64			capacity;
	__u64			used;
	__u64			online_reserved;
	__u64			persistent_reserved[BCH_REPLICAS_MAX];

	__u32			replica_entries_bytes;
	__u32			pad;

	struct bch_replicas_usage replicas[];
};

/* Obsolete */
/*
 * BCH_IOCTL_DEV_USAGE: query device disk space usage
 *
 * Returns disk space usage broken out by data type - both by buckets and
 * sectors.
 */
struct bch_ioctl_dev_usage {
	__u64			dev;
	__u32			flags;
	__u8			state;
	__u8			pad[7];

	__u32			bucket_size;
	__u64			nr_buckets;

	__u64			buckets_ec;

	struct bch_ioctl_dev_usage_type {
		__u64		buckets;
		__u64		sectors;
		__u64		fragmented;
	}			d[10];
};

/* Obsolete */
struct bch_ioctl_dev_usage_v2 {
	__u64			dev;
	__u32			flags;
	__u8			state;
	__u8			nr_data_types;
	__u8			pad[6];

	__u32			bucket_size;
	__u64			nr_buckets;

	struct bch_ioctl_dev_usage_type d[];
};

/*
 * BCH_IOCTL_READ_SUPER: read filesystem superblock
 *
 * Equivalent to reading the superblock directly from the block device, except
 * avoids racing with the kernel writing the superblock or having to figure out
 * which block device to read
 *
 * @sb		- buffer to read into
 * @size	- size of userspace allocated buffer
 * @dev		- device to read superblock for, if BCH_READ_DEV flag is
 *		  specified
 *
 * Returns -ERANGE if buffer provided is too small
 */
struct bch_ioctl_read_super {
	__u32			flags;
	__u32			pad;
	__u64			dev;
	__u64			size;
	__u64			sb;
};

/*
 * BCH_IOCTL_DISK_GET_IDX: give a path to a block device, query filesystem to
 * determine if disk is a (online) member - if so, returns device's index
 *
 * Returns -ENOENT if not found
 */
struct bch_ioctl_disk_get_idx {
	__u64			dev;
};

/*
 * BCH_IOCTL_DISK_RESIZE: resize filesystem on a device
 *
 * @dev		- member to resize
 * @nbuckets	- new number of buckets
 */
struct bch_ioctl_disk_resize {
	__u32			flags;
	__u32			pad;
	__u64			dev;
	__u64			nbuckets;
};

struct bch_ioctl_disk_resize_v2 {
	__u32				flags;
	__u32				pad;
	__u64				dev;
	__u64				nbuckets;
	struct bch_ioctl_err_msg	err;
};

/*
 * BCH_IOCTL_DISK_RESIZE_JOURNAL: resize journal on a device
 *
 * @dev		- member to resize
 * @nbuckets	- new number of buckets
 */
struct bch_ioctl_disk_resize_journal {
	__u32			flags;
	__u32			pad;
	__u64			dev;
	__u64			nbuckets;
};

struct bch_ioctl_disk_resize_journal_v2 {
	__u32				flags;
	__u32				pad;
	__u64				dev;
	__u64				nbuckets;
	struct bch_ioctl_err_msg	err;
};

struct bch_ioctl_subvolume {
	__u32			flags;
	__u32			dirfd;
	__u16			mode;
	__u16			pad[3];
	__u64			dst_ptr;
	__u64			src_ptr;
};

struct bch_ioctl_subvolume_v2 {
	__u32			flags;
	__u32			dirfd;
	__u16			mode;
	__u16			pad[3];
	__u64			dst_ptr;
	__u64			src_ptr;
	struct bch_ioctl_err_msg	err;
};

#define BCH_SUBVOL_SNAPSHOT_CREATE	(1U << 0)
#define BCH_SUBVOL_SNAPSHOT_RO		(1U << 1)

/*
 * BCH_IOCTL_FSCK_OFFLINE: run fsck from the 'bcachefs fsck' userspace command,
 * but with the kernel's implementation of fsck:
 */
struct bch_ioctl_fsck_offline {
	__u64			flags;
	__u64			opts;		/* string */
	__u64			nr_devs;
	/*
	 * Do not re-add __counted_by() here: there's a compiler bug that causes
	 * a bounds check to happen, even when it's a userspace pointer
	 * (properly marked as __user)
	 */
	__u64			devs[];
};

/*
 * BCH_IOCTL_FSCK_ONLINE: run fsck from the 'bcachefs fsck' userspace command,
 * but with the kernel's implementation of fsck:
 */
struct bch_ioctl_fsck_online {
	__u64			flags;
	__u64			opts;		/* string */
};

/*
 * BCH_IOCTL_QUERY_ACCOUNTING: query filesystem disk accounting
 *
 * Returns disk space usage broken out by data type, number of replicas, and
 * by component device
 *
 * @replica_entries_bytes - size, in bytes, allocated for replica usage entries
 *
 * On success, @replica_entries_bytes will be changed to indicate the number of
 * bytes actually used.
 *
 * Returns -ERANGE if @replica_entries_bytes was too small
 */
struct bch_ioctl_query_accounting {
	__u64			capacity;
	__u64			used;
	__u64			online_reserved;

	__u32			accounting_u64s; /* input parameter */
	__u32			accounting_types_mask; /* input parameter */

	struct bkey_i_accounting accounting[];
};

#define BCH_IOCTL_QUERY_COUNTERS_MOUNT	(1 << 0)

struct bch_ioctl_query_counters {
	__u16			nr;
	__u16			flags;
	__u32			pad;
	__u64			d[];
};

struct bch_ioctl_subvol_dirent {
	__u32			reclen;
	__u32			subvolid;
	__u32			flags;
	__u32			snapshot_parent;
	__u64			otime_sec;
	__u32			otime_nsec;
	__u32			pad;
	char			path[];
};

/*
 * The path is NUL-terminated, but reclen is 8-byte aligned so there may
 * be extra NUL padding beyond the terminator.
 */
static inline __u32 bch_ioctl_subvol_dirent_path_len(struct bch_ioctl_subvol_dirent *d)
{
	return strnlen(d->path,
		       d->reclen - offsetof(struct bch_ioctl_subvol_dirent, path));
}

/*
 * BCH_IOCTL_SUBVOLUME_LIST: list child subvolumes of a given parent,
 * readdir style.
 *
 * Parent subvolume is determined from the directory fd used for the ioctl.
 *
 * @pos		- in/out: cursor (child subvolid); 0 to start
 * @buf_size	- size of buffer in bytes
 * @buf		- pointer to userspace buffer for entries
 * @used	- out: bytes written to buffer
 *
 * Each entry in the buffer is a struct bch_ioctl_subvol_dirent with a
 * variable-length NUL-terminated path (relative to the parent subvolume
 * root), padded to 8-byte alignment.
 *
 * Returns 0 on success (used == 0 means no more entries).
 */
struct bch_ioctl_subvol_readdir {
	__u32			pos;
	__u32			buf_size;
	__u64			buf;
	__u32			used;
	__u32			pad;
};

/*
 * BCH_IOCTL_SUBVOLUME_TO_PATH: resolve a subvolume ID to its filesystem path,
 * relative to the filesystem root.
 *
 * The directory fd only selects the filesystem: the path returned does not
 * depend on which directory the fd names, and is not relative to it.
 *
 * @subvolid	- subvolume ID to resolve
 * @buf_size	- size of userspace buffer in bytes
 * @buf		- pointer to userspace buffer for NUL-terminated path
 *
 * Returns 0 on success, -ENOENT if the subvolume doesn't exist or isn't
 * reachable from the fd, -ERANGE if the buffer is too small.
 */
struct bch_ioctl_subvol_to_path {
	__u32			subvolid;
	__u32			buf_size;
	__u64			buf;
};

/*
 * BCH_IOCTL_SNAPSHOT_TREE: return the full snapshot tree (interior + leaf
 * nodes) with per-node disk accounting.
 *
 * @tree_id	- snapshot tree to query; 0 = infer from fd's subvolume
 * @master_subvol - out: master subvolume of this tree
 * @root_snapshot - out: root snapshot ID
 * @nr		- in: capacity of nodes[]; out: entries returned
 * @total	- out: total nodes in tree
 *
 * Returns -ERANGE if nr < total (nr and total are still written back)
 */
/*
 * FROZEN. The ioctl number encodes sizeof(the header), not of the array
 * element, so growing this struct is invisible to the ioctl machinery and
 * silently overruns the buffer of any userspace built against the older
 * layout. It happened once (nr_keys/key_bytes, reverted); don't do it again -
 * add fields to bch_ioctl_snapshot_node_v2, which is size-negotiated.
 */
struct bch_ioctl_snapshot_node {
	__u32			id;		/* snapshot ID */
	__u32			parent;		/* parent snapshot ID, 0 for root */
	__u32			children[2];
	__u32			subvol;		/* subvolume ID, 0 for interior */
	__u32			flags;
	__u32			pad[2];
	/* BCH_DISK_ACCOUNTING_snapshot, summed over the snapshot btrees: */
	__u64			sectors;	/* external (on-disk data) sectors */
};

struct bch_ioctl_snapshot_tree_query {
	__u32			tree_id;	/* in: 0 = infer from fd's subvol */
	__u32			master_subvol;	/* out */
	__u32			root_snapshot;	/* out */
	__u32			nr;		/* in: capacity; out: returned */
	__u32			total;		/* out: total nodes */
	__u32			pad;
	struct bch_ioctl_snapshot_node nodes[];
};

/*
 * v2: same query, but the header states the array element size, so either
 * side can grow bch_ioctl_snapshot_node_v2 without breaking the other.
 * The kernel writes min(node_size, its own sizeof) bytes per entry and
 * strides by node_size, so a shorter node from either direction truncates
 * instead of overrunning. New fields go on the end, and both sides read
 * node_size to know what's present.
 */
struct bch_ioctl_snapshot_node_v2 {
	__u32			id;		/* snapshot ID */
	__u32			parent;		/* parent snapshot ID, 0 for root */
	__u32			children[2];
	__u32			subvol;		/* subvolume ID, 0 for interior */
	__u32			flags;
	__u32			pad[2];
	/* BCH_DISK_ACCOUNTING_snapshot, summed over the snapshot btrees: */
	__u64			sectors;	/* external (on-disk data) sectors */
	__u64			nr_keys;
	__u64			key_bytes;
};

struct bch_ioctl_snapshot_tree_query_v2 {
	__u32			tree_id;	/* in: 0 = infer from fd's subvol */
	__u32			master_subvol;	/* out */
	__u32			root_snapshot;	/* out */
	__u32			nr;		/* in: capacity; out: returned */
	__u32			total;		/* out: total nodes */
	__u32			node_size;	/* in: caller's sizeof; out: ours */
	struct bch_ioctl_snapshot_node_v2 nodes[];
};

/*
 * What a progress indicator is counting: this is uapi because
 * BCH_IOCTL_RECOVERY_STATUS reports it, and a string wouldn't survive the trip.
 */
#define BCH_PROGRESS_UNITS()		\
	x(nodes,		0)	\
	x(keys,			1)

enum bch_progress_units {
#define x(n, v)	BCH_PROGRESS_UNITS_##n = v,
	BCH_PROGRESS_UNITS()
#undef x
};

/*
 * A set of enum bch_recovery_pass ids - the in-memory pass ids, not the stable
 * ids the superblock stores. Bit n of v[0] is pass n, bit n of v[1] is pass
 * 64 + n.
 *
 * 128 bits because 64 is not far off: there are 50 passes today. The kernel's
 * own masks are still u64s, so v[1] reads as zero until those widen - which is
 * the point of having the room here now, so that widening isn't an ABI break.
 */
struct bch_recovery_pass_mask {
	__u64			v[2];
};

/*
 * BCH_IOCTL_RECOVERY_STATUS: what recovery is doing, so a caller can draw a
 * progress display instead of scraping log lines.
 *
 * Only implemented on the status fd (the "status_fd" fsconfig parameter), which
 * is the only handle anyone has on a filesystem that hasn't finished mounting.
 * There's no wakeup for this: poll() on the status fd means "text to read", not
 * "progress moved", so callers poll on a timer.
 *
 * All the @passes_* masks but @passes_scheduled_sb are read under the lock
 * recovery updates them with, so those are mutually consistent with each other
 * and with @pass. @passes_remaining excludes @pass, so the passes this run will
 * have touched is
 *
 *	passes_complete | passes_remaining | {pass}
 *
 * and that denominator grows if a pass reschedules an earlier one: a bar drawn
 * from it can go backwards, which is the truth.
 *
 * @seen and @total ride along outside that lock, so a caller can see one pass's
 * count next to the next pass's id. It's a progress bar. @total is zero when
 * the running pass has no estimate of its own size - draw an indeterminate
 * spinner rather than an empty bar.
 */
struct bch_ioctl_recovery_status {
	struct bch_recovery_pass_mask	passes_scheduled_sb;
	struct bch_recovery_pass_mask	passes_scheduled_ephemeral;
	struct bch_recovery_pass_mask	passes_complete;
	struct bch_recovery_pass_mask	passes_remaining;

	__u32			pass;		/* enum bch_recovery_pass, 0 = idle */
	__u32			units;		/* enum bch_progress_units */
	__u64			seen;
	__u64			total;
};

/*
 * BCHFS_IOC_PREAD_RAW: O_DIRECT read with extended error reporting.
 *
 * Like pread(), but with flags to control error handling and detailed
 * error reporting via the errors bitmask and embedded err_msg.
 *
 * With no flags set, behaves like a normal O_DIRECT read but with
 * better error information.  BCH_PREAD_RAW_no_poison_check bypasses
 * extent poisoning so corrupted data can be recovered.
 */
#define BCH_PREAD_RAW_no_poison_check		(1U << 0)

#define BCH_PREAD_RAW_ERR_checksum		(1U << 0)
#define BCH_PREAD_RAW_ERR_io			(1U << 1)
#define BCH_PREAD_RAW_ERR_decompression		(1U << 2)
#define BCH_PREAD_RAW_ERR_ec_reconstruct	(1U << 3)
struct bch_ioctl_pread_raw {
	__u64				offset;
	__u64				len;
	__u64				buf;		/* userspace data buffer */
	__u32				flags;		/* BCH_PREAD_RAW_* input flags */
	__u32				errors;		/* output: BCH_PREAD_RAW_ERR_* */
	struct bch_ioctl_err_msg	err;
};

/*
 * BCHFS_IOC_UNPOISON: clear the poison flag on extents in a file range.
 *
 * After a checksum error, extents are marked poisoned so subsequent reads
 * return errors without re-reading from disk.  This ioctl clears the
 * poison flag, allowing the data to be read again normally.
 */
struct bch_ioctl_unpoison {
	__u64				offset;
	__u64				len;
	__u32				flags;		/* reserved, must be 0 */
	__u32				pad;
};

/*
 * BCH_IOCTL_QUERY_BTREE_KEYS: read keys from a btree, range query
 *
 * Stateless: the cursor lives in userspace and each call is self-contained,
 * so concurrent callers don't interact and an aborted caller leaves nothing
 * behind. Modeled on BTRFS_IOC_TREE_SEARCH / FS_IOC_GETFSMAP.
 *
 * The buffer is filled with bkeys (struct bkey_i: unpacked key header
 * followed by the value), densely packed; step to the next with bkey_bytes()
 * (k->u64s * 8). This is the unpacked in-memory format - the same format the
 * update ioctls and libbcachefs speak, not the packed on-disk one.
 *
 * Flags:
 *
 * slots: iterate positions instead of keys - a KEY_TYPE_deleted key is
 * synthesized for every position without one. On extents btrees a single
 * synthesized key covers each hole. This is how you see holes; mind that a
 * wide range in slots mode returns a key per position.
 *
 * prev: iterate backwards, from @start down to @end.
 *
 * all_snapshots: return keys from all snapshots, rather than filtering to
 * the snapshot in @start. Snapshot-filtered iteration (i.e. without this
 * flag, on a snapshots btree) has preconditions, -EINVAL otherwise:
 * @start.snapshot must be nonzero (there's no snapshot to filter against),
 * @end must not be POS_MAX (whiteout filtering peeks ahead of the end pos),
 * and with prev the range must be within a single inode. Interior node
 * levels (@level > 0) always iterate all snapshots.
 *
 * @btree	- btree id
 * @level	- btree level to read keys from (0 = leaves)
 * @flags	- BCH_IOCTL_QUERY_BTREE_KEYS_*
 * @done	- out: nonzero once iteration has reached the end of the range
 * @start	- in/out: cursor; on return, where the next call should
 *		  resume (only meaningful while @done is unset)
 * @end		- inclusive bound: upper, or lower with prev
 * @buf		- pointer to userspace buffer for keys
 * @buf_size	- size of buffer in bytes
 * @used	- out: bytes written to buffer
 *
 * To iterate: repeat the call, keeping @start from the previous call, until
 * @done is set. The kernel bounds how much it returns per call, so @used may
 * be well short of @buf_size while more keys remain.
 *
 * Returns -ERANGE if @buf_size is too small to hold even one key.
 */
#define BCH_IOCTL_QUERY_BTREE_KEYS_slots		(1U << 0)
#define BCH_IOCTL_QUERY_BTREE_KEYS_prev			(1U << 1)
#define BCH_IOCTL_QUERY_BTREE_KEYS_all_snapshots	(1U << 2)
#define BCH_IOCTL_QUERY_BTREE_KEYS_nofilter_whiteouts	(1U << 3)

struct bch_ioctl_query_btree_keys {
	__u32			btree;
	__u32			level;
	__u32			flags;
	__u32			done;
	struct bpos		start;
	struct bpos		end;
	__u64			buf;
	__u32			buf_size;
	__u32			used;
};

#endif /* _BCACHEFS_IOCTL_H */
