use core::cmp::Ordering;
use core::fmt;
use core::fmt::Display;
#[cfg(feature = "std")]
use std::error;
use crate::phys::{NvDecodeError, NvList};
use crate::util::Fstr;
#[derive(Debug, Eq, PartialEq)]
pub enum Compatibility<'a> {
Off,
Legacy,
Files(&'a str),
}
impl Compatibility<'_> {
pub fn from(compatibility: &str) -> Compatibility<'_> {
match compatibility {
"off" => Compatibility::Off {},
"legacy" => Compatibility::Legacy {},
_ => Compatibility::Files(compatibility),
}
}
}
impl<'a> Compatibility<'a> {
pub fn iter(&self) -> CompatibilityIterator<'a> {
CompatibilityIterator {
compatibility: match self {
Compatibility::Off => Compatibility::Off,
Compatibility::Legacy => Compatibility::Legacy,
Compatibility::Files(files) => Compatibility::Files(files),
},
index: 0,
}
}
}
impl<'a> From<&Compatibility<'a>> for &'a str {
fn from(val: &Compatibility<'a>) -> &'a str {
match val {
Compatibility::Off => "off",
Compatibility::Legacy => "legacy",
Compatibility::Files(compatibility) => compatibility,
}
}
}
impl<'a> From<Compatibility<'a>> for &'a str {
fn from(val: Compatibility<'a>) -> &'a str {
(&val).into()
}
}
impl Display for Compatibility<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s: &str = self.into();
write!(f, "{s}")
}
}
#[derive(Debug)]
pub struct CompatibilityIterator<'a> {
compatibility: Compatibility<'a>,
index: usize,
}
impl<'a> IntoIterator for Compatibility<'a> {
type Item = &'a str;
type IntoIter = CompatibilityIterator<'a>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<'a> Iterator for CompatibilityIterator<'a> {
type Item = &'a str;
fn next(&mut self) -> Option<<Self as Iterator>::Item> {
match self.compatibility {
Compatibility::Off => None,
Compatibility::Legacy => None,
Compatibility::Files(comp) => {
match (self.index).cmp(&comp.len()) {
Ordering::Greater => None,
Ordering::Equal => {
self.index += 1;
Some("")
}
Ordering::Less => {
let suffix = &comp[self.index..];
match suffix.find(',') {
None => {
self.index = comp.len() + 1;
Some(suffix)
}
Some(index) => {
self.index += index + 1;
Some(&suffix[0..index])
}
}
}
}
}
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Feature {
AllocationClasses,
AsyncDestroy,
Blake3,
BlockCloning,
BookmarkV2,
BookmarkWritten,
Bookmarks,
DeviceRebuild,
DeviceRemoval,
Draid,
Edonr,
EmbeddedData,
EmptyBlockPointerObject,
EnabledTxg,
Encryption,
ExtensibleDataset,
FilesystemLimits,
HeadErrorLog,
HoleBirth,
LargeBlocks,
LargeDnode,
LiveList,
LogSpaceMap,
Lz4Compress,
MultiVdevCrashDump,
ObsoleteCounts,
ProjectQuota,
RaidzExpansion,
RedactedDatasets,
RedactionListSpill,
RedactionBookmarks,
ResilverDefer,
Sha512,
Skein,
SpacemapHistogram,
SpacemapV2,
UserObjectAccounting,
VdevZapsV2,
ZilSaXattr,
ZpoolCheckpoint,
ZstdCompress,
}
const ALL_FEATURES: [Feature; 41] = [
Feature::AllocationClasses,
Feature::AsyncDestroy,
Feature::Blake3,
Feature::BlockCloning,
Feature::BookmarkV2,
Feature::BookmarkWritten,
Feature::Bookmarks,
Feature::DeviceRebuild,
Feature::DeviceRemoval,
Feature::Draid,
Feature::Edonr,
Feature::EmbeddedData,
Feature::EmptyBlockPointerObject,
Feature::EnabledTxg,
Feature::Encryption,
Feature::ExtensibleDataset,
Feature::FilesystemLimits,
Feature::HeadErrorLog,
Feature::HoleBirth,
Feature::LargeBlocks,
Feature::LargeDnode,
Feature::LiveList,
Feature::LogSpaceMap,
Feature::Lz4Compress,
Feature::MultiVdevCrashDump,
Feature::ObsoleteCounts,
Feature::ProjectQuota,
Feature::RaidzExpansion,
Feature::RedactedDatasets,
Feature::RedactionBookmarks,
Feature::RedactionListSpill,
Feature::ResilverDefer,
Feature::Sha512,
Feature::Skein,
Feature::SpacemapHistogram,
Feature::SpacemapV2,
Feature::UserObjectAccounting,
Feature::VdevZapsV2,
Feature::ZilSaXattr,
Feature::ZpoolCheckpoint,
Feature::ZstdCompress,
];
impl Feature {
pub fn all() -> &'static [Feature] {
&ALL_FEATURES
}
}
impl Display for Feature {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s: &'static str = (*self).into();
write!(f, "{s}")
}
}
impl From<Feature> for &'static str {
fn from(val: Feature) -> &'static str {
match val {
Feature::AllocationClasses => "org.zfsonlinux:allocation_classes",
Feature::AsyncDestroy => "com.delphix:async_destroy",
Feature::Blake3 => "org.openzfs:blake3",
Feature::BlockCloning => "com.fudosecurity:block_cloning",
Feature::BookmarkV2 => "com.datto:bookmark_v2",
Feature::BookmarkWritten => "com.delphix:bookmark_written",
Feature::Bookmarks => "com.delphix:bookmarks",
Feature::DeviceRebuild => "org.openzfs:device_rebuild",
Feature::DeviceRemoval => "com.delphix:device_removal",
Feature::Draid => "org.openzfs:draid",
Feature::Edonr => "org.illumos:edonr",
Feature::EmbeddedData => "com.delphix:embedded_data",
Feature::EmptyBlockPointerObject => "com.delphix:empty_bpobj",
Feature::EnabledTxg => "com.delphix:enabled_txg",
Feature::Encryption => "com.datto:encryption",
Feature::ExtensibleDataset => "com.delphix:extensible_dataset",
Feature::FilesystemLimits => "com.joyent:filesystem_limits",
Feature::HeadErrorLog => "com.delphix:head_errlog",
Feature::HoleBirth => "com.delphix:hole_birth",
Feature::LargeBlocks => "org.open-zfs:large_blocks",
Feature::LargeDnode => "org.zfsonlinux:large_dnode",
Feature::LiveList => "com.delphix:livelist",
Feature::LogSpaceMap => "com.delphix:log_spacemap",
Feature::Lz4Compress => "org.illumos:lz4_compress",
Feature::MultiVdevCrashDump => "com.joyent:multi_vdev_crash_dump",
Feature::ObsoleteCounts => "com.delphix:obsolete_counts",
Feature::ProjectQuota => "org.zfsonlinux:project_quota",
Feature::RaidzExpansion => "org.openzfs:raidz_expansion",
Feature::RedactedDatasets => "com.delphix:redacted_datasets",
Feature::RedactionBookmarks => "com.delphix:redaction_bookmarks",
Feature::RedactionListSpill => "com.delphix:redaction_list_spill",
Feature::ResilverDefer => "com.datto:resilver_defer",
Feature::Sha512 => "org.illumos:sha512",
Feature::Skein => "org.illumos:skein",
Feature::SpacemapHistogram => "com.delphix:spacemap_histogram",
Feature::SpacemapV2 => "com.delphix:spacemap_v2",
Feature::UserObjectAccounting => "org.zfsonlinux:userobj_accounting",
Feature::VdevZapsV2 => "com.klarasystems:vdev_zaps_v2",
Feature::ZilSaXattr => "org.openzfs:zilsaxattr",
Feature::ZpoolCheckpoint => "com.delphix:zpool_checkpoint",
Feature::ZstdCompress => "org.freebsd:zstd_compress",
}
}
}
impl TryFrom<&str> for Feature {
type Error = FeatureDecodeError;
fn try_from(feature: &str) -> Result<Self, Self::Error> {
match feature {
"org.zfsonlinux:allocation_classes" => Ok(Feature::AllocationClasses),
"com.delphix:async_destroy" => Ok(Feature::AsyncDestroy),
"org.openzfs:blake3" => Ok(Feature::Blake3),
"com.fudosecurity:block_cloning" => Ok(Feature::BlockCloning),
"com.datto:bookmark_v2" => Ok(Feature::BookmarkV2),
"com.delphix:bookmark_written" => Ok(Feature::BookmarkWritten),
"com.delphix:bookmarks" => Ok(Feature::Bookmarks),
"org.openzfs:device_rebuild" => Ok(Feature::DeviceRebuild),
"com.delphix:device_removal" => Ok(Feature::DeviceRemoval),
"org.openzfs:draid" => Ok(Feature::Draid),
"org.illumos:edonr" => Ok(Feature::Edonr),
"com.delphix:embedded_data" => Ok(Feature::EmbeddedData),
"com.delphix:empty_bpobj" => Ok(Feature::EmptyBlockPointerObject),
"com.delphix:enabled_txg" => Ok(Feature::EnabledTxg),
"com.datto:encryption" => Ok(Feature::Encryption),
"com.delphix:extensible_dataset" => Ok(Feature::ExtensibleDataset),
"com.joyent:filesystem_limits" => Ok(Feature::FilesystemLimits),
"com.delphix:head_errlog" => Ok(Feature::HeadErrorLog),
"com.delphix:hole_birth" => Ok(Feature::HoleBirth),
"org.open-zfs:large_blocks" => Ok(Feature::LargeBlocks),
"org.zfsonlinux:large_dnode" => Ok(Feature::LargeDnode),
"com.delphix:livelist" => Ok(Feature::LiveList),
"com.delphix:log_spacemap" => Ok(Feature::LogSpaceMap),
"org.illumos:lz4_compress" => Ok(Feature::Lz4Compress),
"com.joyent:multi_vdev_crash_dump" => Ok(Feature::MultiVdevCrashDump),
"com.delphix:obsolete_counts" => Ok(Feature::ObsoleteCounts),
"org.zfsonlinux:project_quota" => Ok(Feature::ProjectQuota),
"org.openzfs:raidz_expansion" => Ok(Feature::RaidzExpansion),
"com.delphix:redacted_datasets" => Ok(Feature::RedactedDatasets),
"com.delphix:redaction_bookmarks" => Ok(Feature::RedactionBookmarks),
"com.delphix:redaction_list_spill" => Ok(Feature::RedactionListSpill),
"com.datto:resilver_defer" => Ok(Feature::ResilverDefer),
"org.illumos:sha512" => Ok(Feature::Sha512),
"org.illumos:skein" => Ok(Feature::Skein),
"com.delphix:spacemap_histogram" => Ok(Feature::SpacemapHistogram),
"com.delphix:spacemap_v2" => Ok(Feature::SpacemapV2),
"org.zfsonlinux:userobj_accounting" => Ok(Feature::UserObjectAccounting),
"com.klarasystems:vdev_zaps_v2" => Ok(Feature::VdevZapsV2),
"org.openzfs:zilsaxattr" => Ok(Feature::ZilSaXattr),
"com.delphix:zpool_checkpoint" => Ok(Feature::ZpoolCheckpoint),
"org.freebsd:zstd_compress" => Ok(Feature::ZstdCompress),
_ => Err(FeatureDecodeError::Unknown {}),
}
}
}
#[derive(Debug)]
pub enum FeatureDecodeError {
Unknown {},
}
impl fmt::Display for FeatureDecodeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
FeatureDecodeError::Unknown {} => {
write!(f, "Feature decode error, unknown feature")
}
}
}
}
#[cfg(feature = "std")]
impl error::Error for FeatureDecodeError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
None
}
}
#[derive(Clone, Copy, Default)]
pub struct FeatureSet {
features: u64,
}
impl fmt::Debug for FeatureSet {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut f = f.debug_struct("FeatureSet");
for feature in self.iter() {
f.field("feature", &feature);
}
f.finish()
}
}
impl FeatureSet {
pub fn len(&self) -> usize {
self.features.count_ones().try_into().unwrap()
}
pub fn is_empty(&self) -> bool {
self.features == 0
}
pub fn iter(&self) -> FeatureSetIterator {
FeatureSetIterator {
feature_set: *self,
index: 0,
}
}
pub fn insert(&mut self, feature: Feature) -> bool {
let mask = 1 << FeatureSet::feature_to_bit_shift(feature);
if (self.features & mask) == 0 {
self.features |= mask;
true
} else {
false
}
}
pub fn remove(&mut self, feature: Feature) -> bool {
let mask = 1 << FeatureSet::feature_to_bit_shift(feature);
if (self.features & mask) == 0 {
false
} else {
self.features &= u64::MAX ^ mask;
true
}
}
pub fn contains(&self, feature: Feature) -> bool {
let mask = 1 << FeatureSet::feature_to_bit_shift(feature);
(self.features & mask) != 0
}
pub fn from_nv_list(list: &NvList<'_>) -> Result<FeatureSet, FeatureSetDecodeError> {
let mut features = 0;
for pair_res in list {
let pair = pair_res?;
pair.get_bool_flag()?;
let feature = match Feature::try_from(pair.name) {
Ok(v) => v,
Err(_) => {
return Err(FeatureSetDecodeError::Unknown {
feature: pair.name.into(),
})
}
};
features |= 1 << FeatureSet::feature_to_bit_shift(feature);
}
Ok(FeatureSet { features })
}
fn feature_to_bit_shift(feature: Feature) -> u32 {
match feature {
Feature::AllocationClasses => 0,
Feature::AsyncDestroy => 1,
Feature::Blake3 => 2,
Feature::BlockCloning => 3,
Feature::BookmarkV2 => 4,
Feature::BookmarkWritten => 5,
Feature::Bookmarks => 6,
Feature::DeviceRebuild => 7,
Feature::DeviceRemoval => 8,
Feature::Draid => 9,
Feature::Edonr => 10,
Feature::EmbeddedData => 11,
Feature::EmptyBlockPointerObject => 12,
Feature::EnabledTxg => 13,
Feature::Encryption => 14,
Feature::ExtensibleDataset => 15,
Feature::FilesystemLimits => 16,
Feature::HeadErrorLog => 17,
Feature::HoleBirth => 18,
Feature::LargeBlocks => 19,
Feature::LargeDnode => 20,
Feature::LiveList => 21,
Feature::LogSpaceMap => 22,
Feature::Lz4Compress => 23,
Feature::MultiVdevCrashDump => 24,
Feature::ObsoleteCounts => 25,
Feature::ProjectQuota => 26,
Feature::RaidzExpansion => 27,
Feature::RedactedDatasets => 28,
Feature::RedactionBookmarks => 29,
Feature::RedactionListSpill => 30,
Feature::ResilverDefer => 31,
Feature::Sha512 => 32,
Feature::Skein => 33,
Feature::SpacemapHistogram => 34,
Feature::SpacemapV2 => 35,
Feature::UserObjectAccounting => 36,
Feature::VdevZapsV2 => 37,
Feature::ZilSaXattr => 38,
Feature::ZpoolCheckpoint => 39,
Feature::ZstdCompress => 40,
}
}
}
#[derive(Debug)]
pub enum FeatureSetDecodeError {
Feature {
err: FeatureDecodeError,
},
Nv {
err: NvDecodeError,
},
Unknown {
feature: Fstr<16>,
},
}
impl From<NvDecodeError> for FeatureSetDecodeError {
fn from(err: NvDecodeError) -> Self {
FeatureSetDecodeError::Nv { err }
}
}
impl From<FeatureDecodeError> for FeatureSetDecodeError {
fn from(err: FeatureDecodeError) -> Self {
FeatureSetDecodeError::Feature { err }
}
}
impl fmt::Display for FeatureSetDecodeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
FeatureSetDecodeError::Feature { err } => {
write!(f, "FeatureSet decode error | {err}")
}
FeatureSetDecodeError::Nv { err } => {
write!(f, "FeatureSet decode error | {err}")
}
FeatureSetDecodeError::Unknown { feature } => {
write!(f, "FeatureSet decode error, unknown feature '{feature}'")
}
}
}
}
#[cfg(feature = "std")]
impl error::Error for FeatureSetDecodeError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
FeatureSetDecodeError::Feature { err } => Some(err),
FeatureSetDecodeError::Nv { err } => Some(err),
_ => None,
}
}
}
#[derive(Debug)]
pub struct FeatureSetIterator {
feature_set: FeatureSet,
index: usize,
}
impl IntoIterator for FeatureSet {
type Item = Feature;
type IntoIter = FeatureSetIterator;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl Iterator for FeatureSetIterator {
type Item = Feature;
fn next(&mut self) -> Option<Self::Item> {
while self.index < ALL_FEATURES.len() {
let feature = ALL_FEATURES[self.index];
self.index += 1;
if self.feature_set.contains(feature) {
return Some(feature);
}
}
None
}
}