diff --git a/SOURCES/dmpd-Count-under-populated-nodes-if-the-option-is-provided.patch b/SOURCES/dmpd-Count-under-populated-nodes-if-the-option-is-provided.patch new file mode 100644 index 0000000..f250dd3 --- /dev/null +++ b/SOURCES/dmpd-Count-under-populated-nodes-if-the-option-is-provided.patch @@ -0,0 +1,152 @@ + persistent-data/data-structures/btree_counter.h | 22 ++++++++++++++++------ + thin-provisioning/metadata_counter.cc | 14 ++++++++------ + thin-provisioning/metadata_counter.h | 3 ++- + thin-provisioning/thin_check.cc | 2 +- + 4 files changed, 27 insertions(+), 14 deletions(-) + +diff --git a/persistent-data/data-structures/btree_counter.h b/persistent-data/data-structures/btree_counter.h +index 6ccf03a..e639c40 100644 +--- a/persistent-data/data-structures/btree_counter.h ++++ b/persistent-data/data-structures/btree_counter.h +@@ -14,9 +14,11 @@ namespace persistent_data { + public: + typedef btree tree; + +- counting_visitor(block_counter &bc, ValueCounter &vc) ++ counting_visitor(block_counter &bc, ValueCounter &vc, ++ bool ignore_non_fatal = false) + : bc_(bc), +- vc_(vc) { ++ vc_(vc), ++ ignore_non_fatal_(ignore_non_fatal) { + } + + virtual bool visit_internal(node_location const &l, +@@ -63,7 +65,7 @@ namespace persistent_data { + if (!checker_.check_block_nr(n) || + !checker_.check_value_size(n) || + !checker_.check_max_entries(n) || +- !checker_.check_nr_entries(n, l.is_sub_root()) || ++ !check_nr_entries(l, n) || + !checker_.check_ordered_keys(n) || + !checker_.check_parent_key(n, l.is_sub_root() ? boost::optional() : l.key)) + return false; +@@ -80,7 +82,7 @@ namespace persistent_data { + if (!checker_.check_block_nr(n) || + !checker_.check_value_size(n) || + !checker_.check_max_entries(n) || +- !checker_.check_nr_entries(n, l.is_sub_root()) || ++ !check_nr_entries(l, n) || + !checker_.check_ordered_keys(n) || + !checker_.check_parent_key(n, l.is_sub_root() ? boost::optional() : l.key) || + !checker_.check_leaf_key(n, last_leaf_key_[l.level()])) +@@ -106,10 +108,17 @@ namespace persistent_data { + return !seen; + } + ++ template ++ bool check_nr_entries(node_location const &loc, ++ btree_detail::node_ref const &n) { ++ return ignore_non_fatal_ || checker_.check_nr_entries(n, loc.is_sub_root()); ++ } ++ + block_counter &bc_; + ValueCounter &vc_; + btree_node_checker checker_; + boost::optional last_leaf_key_[Levels]; ++ bool ignore_non_fatal_; + }; + } + +@@ -137,8 +146,9 @@ namespace persistent_data { + // walked. This walk should only be done once you're sure the tree + // is not corrupt. + template +- void count_btree_blocks(btree const &tree, block_counter &bc, ValueCounter &vc) { +- btree_count_detail::counting_visitor v(bc, vc); ++ void count_btree_blocks(btree const &tree, block_counter &bc, ValueCounter &vc, ++ bool ignore_non_fatal = false) { ++ btree_count_detail::counting_visitor v(bc, vc, ignore_non_fatal); + tree.visit_depth_first(v); + } + } +diff --git a/thin-provisioning/metadata_counter.cc b/thin-provisioning/metadata_counter.cc +index 95487d2..6f0d109 100644 +--- a/thin-provisioning/metadata_counter.cc ++++ b/thin-provisioning/metadata_counter.cc +@@ -10,14 +10,15 @@ using namespace thin_provisioning; + namespace { + void count_trees(transaction_manager::ptr tm, + superblock_detail::superblock const &sb, +- block_counter &bc) { ++ block_counter &bc, ++ bool ignore_non_fatal) { + + // Count the device tree + { + noop_value_counter vc; + device_tree dtree(*tm, sb.device_details_root_, + device_tree_detail::device_details_traits::ref_counter()); +- count_btree_blocks(dtree, bc, vc); ++ count_btree_blocks(dtree, bc, vc, ignore_non_fatal); + } + + // Count the mapping tree +@@ -25,7 +26,7 @@ namespace { + noop_value_counter vc; + mapping_tree mtree(*tm, sb.data_mapping_root_, + mapping_tree_detail::block_traits::ref_counter(space_map::ptr())); +- count_btree_blocks(mtree, bc, vc); ++ count_btree_blocks(mtree, bc, vc, ignore_non_fatal); + } + } + +@@ -55,17 +56,18 @@ namespace { + void thin_provisioning::count_metadata(transaction_manager::ptr tm, + superblock_detail::superblock const &sb, + block_counter &bc, +- bool skip_metadata_snap) { ++ bool skip_metadata_snap, ++ bool ignore_non_fatal) { + // Count the superblock + bc.inc(superblock_detail::SUPERBLOCK_LOCATION); +- count_trees(tm, sb, bc); ++ count_trees(tm, sb, bc, ignore_non_fatal); + + // Count the metadata snap, if present + if (!skip_metadata_snap && sb.metadata_snap_ != superblock_detail::SUPERBLOCK_LOCATION) { + bc.inc(sb.metadata_snap_); + + superblock_detail::superblock snap = read_superblock(tm->get_bm(), sb.metadata_snap_); +- count_trees(tm, snap, bc); ++ count_trees(tm, snap, bc, ignore_non_fatal); + } + + count_space_maps(tm, sb, bc); +diff --git a/thin-provisioning/metadata_counter.h b/thin-provisioning/metadata_counter.h +index bc65ab9..ea9813d 100644 +--- a/thin-provisioning/metadata_counter.h ++++ b/thin-provisioning/metadata_counter.h +@@ -10,7 +10,8 @@ namespace thin_provisioning { + void count_metadata(transaction_manager::ptr tm, + superblock_detail::superblock const &sb, + block_counter &bc, +- bool skip_metadata_snap = false); ++ bool skip_metadata_snap = false, ++ bool ignore_non_fatal = false); + } + + //---------------------------------------------------------------- +diff --git a/thin-provisioning/thin_check.cc b/thin-provisioning/thin_check.cc +index c2612b5..5900902 100644 +--- a/thin-provisioning/thin_check.cc ++++ b/thin-provisioning/thin_check.cc +@@ -165,7 +165,7 @@ namespace { + transaction_manager::ptr tm) { + block_counter bc; + +- count_metadata(tm, sb, bc); ++ count_metadata(tm, sb, bc, false, fs.ignore_non_fatal_errors); + + // Finally we need to check the metadata space map agrees + // with the counts we've just calculated. diff --git a/SOURCES/dmpd-thin_check-Under-populated-nodes-are-now-non-fatal.patch b/SOURCES/dmpd-thin_check-Under-populated-nodes-are-now-non-fatal.patch new file mode 100644 index 0000000..37d5c5f --- /dev/null +++ b/SOURCES/dmpd-thin_check-Under-populated-nodes-are-now-non-fatal.patch @@ -0,0 +1,356 @@ + .../data-structures/btree_damage_visitor.h | 22 +++++++++++----- + thin-provisioning/device_tree.cc | 11 +++++--- + thin-provisioning/device_tree.h | 6 +++-- + thin-provisioning/mapping_tree.cc | 30 +++++++++++++--------- + thin-provisioning/mapping_tree.h | 25 ++++++++++++------ + thin-provisioning/metadata_dumper.cc | 16 ++++++------ + thin-provisioning/thin_check.cc | 6 ++--- + 7 files changed, 73 insertions(+), 43 deletions(-) + +diff --git a/persistent-data/data-structures/btree_damage_visitor.h b/persistent-data/data-structures/btree_damage_visitor.h +index 1ff16a8..60e25d9 100644 +--- a/persistent-data/data-structures/btree_damage_visitor.h ++++ b/persistent-data/data-structures/btree_damage_visitor.h +@@ -159,10 +159,12 @@ namespace persistent_data { + typedef boost::optional maybe_run64; + + btree_damage_visitor(ValueVisitor &value_visitor, +- DamageVisitor &damage_visitor) ++ DamageVisitor &damage_visitor, ++ bool ignore_non_fatal) + : avoid_repeated_visits_(true), + value_visitor_(value_visitor), +- damage_visitor_(damage_visitor) { ++ damage_visitor_(damage_visitor), ++ ignore_non_fatal_(ignore_non_fatal) { + } + + bool visit_internal(node_location const &loc, +@@ -231,7 +233,7 @@ namespace persistent_data { + else if (!checker_.check_block_nr(n) || + !checker_.check_value_size(n) || + !checker_.check_max_entries(n) || +- !checker_.check_nr_entries(n, loc.is_sub_root()) || ++ !check_nr_entries(loc, n) || + !checker_.check_ordered_keys(n) || + !checker_.check_parent_key(n, loc.is_sub_root() ? boost::optional() : loc.key)) { + report_damage(checker_.get_last_error_string()); +@@ -255,7 +257,7 @@ namespace persistent_data { + else if (!checker_.check_block_nr(n) || + !checker_.check_value_size(n) || + !checker_.check_max_entries(n) || +- !checker_.check_nr_entries(n, loc.is_sub_root()) || ++ !check_nr_entries(loc, n) || + !checker_.check_ordered_keys(n) || + !checker_.check_parent_key(n, loc.is_sub_root() ? boost::optional() : loc.key) || + !checker_.check_leaf_key(n, last_leaf_key_[loc.level()])) { +@@ -353,6 +355,12 @@ namespace persistent_data { + maybe_issue_damage(*old_path); + } + ++ template ++ bool check_nr_entries(node_location const &loc, ++ btree_detail::node_ref const &n) { ++ return ignore_non_fatal_ || checker_.check_nr_entries(n, loc.is_sub_root()); ++ } ++ + //-------------------------------- + + bool avoid_repeated_visits_; +@@ -367,15 +375,17 @@ namespace persistent_data { + path_tracker path_tracker_; + damage_tracker dt_; + std::list damage_reasons_; ++ bool ignore_non_fatal_; + }; + } + + template + void btree_visit_values(btree const &tree, + ValueVisitor &value_visitor, +- DamageVisitor &damage_visitor) { ++ DamageVisitor &damage_visitor, ++ bool ignore_non_fatal = false) { + btree_detail::btree_damage_visitor +- v(value_visitor, damage_visitor); ++ v(value_visitor, damage_visitor, ignore_non_fatal); + tree.visit_depth_first(v); + } + } +diff --git a/thin-provisioning/device_tree.cc b/thin-provisioning/device_tree.cc +index 4837cb7..8ae16f7 100644 +--- a/thin-provisioning/device_tree.cc ++++ b/thin-provisioning/device_tree.cc +@@ -89,18 +89,21 @@ namespace thin_provisioning { + void + thin_provisioning::walk_device_tree(device_tree const &tree, + device_tree_detail::device_visitor &vv, +- device_tree_detail::damage_visitor &dv) ++ device_tree_detail::damage_visitor &dv, ++ bool ignore_non_fatal) + { + visitor_adapter av(vv); + ll_damage_visitor ll_dv(dv); +- btree_visit_values(tree, av, ll_dv); ++ btree_visit_values(tree, av, ll_dv, ignore_non_fatal); + } + + void +-thin_provisioning::check_device_tree(device_tree const &tree, damage_visitor &visitor) ++thin_provisioning::check_device_tree(device_tree const &tree, ++ damage_visitor &visitor, ++ bool ignore_non_fatal) + { + noop_visitor vv; +- walk_device_tree(tree, vv, visitor); ++ walk_device_tree(tree, vv, visitor, ignore_non_fatal); + } + + //---------------------------------------------------------------- +diff --git a/thin-provisioning/device_tree.h b/thin-provisioning/device_tree.h +index ec0f9f2..980f1a9 100644 +--- a/thin-provisioning/device_tree.h ++++ b/thin-provisioning/device_tree.h +@@ -74,9 +74,11 @@ namespace thin_provisioning { + + void walk_device_tree(device_tree const &tree, + device_tree_detail::device_visitor &dev_v, +- device_tree_detail::damage_visitor &dv); ++ device_tree_detail::damage_visitor &dv, ++ bool ignore_non_fatal = false); + void check_device_tree(device_tree const &tree, +- device_tree_detail::damage_visitor &visitor); ++ device_tree_detail::damage_visitor &visitor, ++ bool ignore_non_fatal = false); + } + + //---------------------------------------------------------------- +diff --git a/thin-provisioning/mapping_tree.cc b/thin-provisioning/mapping_tree.cc +index 5b69fe9..6c82095 100644 +--- a/thin-provisioning/mapping_tree.cc ++++ b/thin-provisioning/mapping_tree.cc +@@ -220,54 +220,60 @@ namespace { + void + thin_provisioning::walk_mapping_tree(dev_tree const &tree, + mapping_tree_detail::device_visitor &dev_v, +- mapping_tree_detail::damage_visitor &dv) ++ mapping_tree_detail::damage_visitor &dv, ++ bool ignore_non_fatal) + { + dev_tree_damage_visitor ll_dv(dv); +- btree_visit_values(tree, dev_v, ll_dv); ++ btree_visit_values(tree, dev_v, ll_dv, ignore_non_fatal); + } + + void + thin_provisioning::check_mapping_tree(dev_tree const &tree, +- mapping_tree_detail::damage_visitor &visitor) ++ mapping_tree_detail::damage_visitor &visitor, ++ bool ignore_non_fatal) + { + noop_block_visitor dev_v; +- walk_mapping_tree(tree, dev_v, visitor); ++ walk_mapping_tree(tree, dev_v, visitor, ignore_non_fatal); + } + + void + thin_provisioning::walk_mapping_tree(mapping_tree const &tree, + mapping_tree_detail::mapping_visitor &mv, +- mapping_tree_detail::damage_visitor &dv) ++ mapping_tree_detail::damage_visitor &dv, ++ bool ignore_non_fatal) + { + mapping_tree_damage_visitor ll_dv(dv); +- btree_visit_values(tree, mv, ll_dv); ++ btree_visit_values(tree, mv, ll_dv, ignore_non_fatal); + } + + void + thin_provisioning::check_mapping_tree(mapping_tree const &tree, +- mapping_tree_detail::damage_visitor &visitor) ++ mapping_tree_detail::damage_visitor &visitor, ++ bool ignore_non_fatal) + { + noop_block_time_visitor mv; +- walk_mapping_tree(tree, mv, visitor); ++ walk_mapping_tree(tree, mv, visitor, ignore_non_fatal); + } + + void + thin_provisioning::walk_mapping_tree(single_mapping_tree const &tree, + uint64_t dev_id, + mapping_tree_detail::mapping_visitor &mv, +- mapping_tree_detail::damage_visitor &dv) ++ mapping_tree_detail::damage_visitor &dv, ++ bool ignore_non_fatal) + { + single_mapping_tree_damage_visitor ll_dv(dv, dev_id); +- btree_visit_values(tree, mv, ll_dv); ++ btree_visit_values(tree, mv, ll_dv, ignore_non_fatal); + } + + void + thin_provisioning::check_mapping_tree(single_mapping_tree const &tree, + uint64_t dev_id, +- mapping_tree_detail::damage_visitor &visitor) ++ mapping_tree_detail::damage_visitor &visitor, ++ bool ignore_non_fatal) + { + noop_block_time_visitor mv; +- walk_mapping_tree(tree, dev_id, mv, visitor); ++ walk_mapping_tree(tree, dev_id, mv, visitor, ignore_non_fatal); + } + + //---------------------------------------------------------------- +diff --git a/thin-provisioning/mapping_tree.h b/thin-provisioning/mapping_tree.h +index ce88ba2..68b5c4b 100644 +--- a/thin-provisioning/mapping_tree.h ++++ b/thin-provisioning/mapping_tree.h +@@ -128,23 +128,32 @@ namespace thin_provisioning { + + void walk_mapping_tree(dev_tree const &tree, + mapping_tree_detail::device_visitor &dev_v, +- mapping_tree_detail::damage_visitor &dv); +- void check_mapping_tree(dev_tree const &tree, +- mapping_tree_detail::damage_visitor &visitor); ++ mapping_tree_detail::damage_visitor &dv, ++ bool ignore_non_fatal = false); + + void walk_mapping_tree(mapping_tree const &tree, + mapping_tree_detail::mapping_visitor &mv, +- mapping_tree_detail::damage_visitor &dv); +- void check_mapping_tree(mapping_tree const &tree, +- mapping_tree_detail::damage_visitor &visitor); ++ mapping_tree_detail::damage_visitor &dv, ++ bool ignore_non_fatal = false); + + void walk_mapping_tree(single_mapping_tree const &tree, + uint64_t dev_id, + mapping_tree_detail::mapping_visitor &mv, +- mapping_tree_detail::damage_visitor &dv); ++ mapping_tree_detail::damage_visitor &dv, ++ bool ignore_non_fatal = false); ++ + void check_mapping_tree(single_mapping_tree const &tree, + uint64_t dev_id, +- mapping_tree_detail::damage_visitor &visitor); ++ mapping_tree_detail::damage_visitor &visitor, ++ bool ignore_non_fatal = false); ++ ++ void check_mapping_tree(dev_tree const &tree, ++ mapping_tree_detail::damage_visitor &visitor, ++ bool ignore_non_fatal = false); ++ ++ void check_mapping_tree(mapping_tree const &tree, ++ mapping_tree_detail::damage_visitor &visitor, ++ bool ignore_non_fatal = false); + } + + //---------------------------------------------------------------- +diff --git a/thin-provisioning/metadata_dumper.cc b/thin-provisioning/metadata_dumper.cc +index ce28d2e..9ebc8d8 100644 +--- a/thin-provisioning/metadata_dumper.cc ++++ b/thin-provisioning/metadata_dumper.cc +@@ -131,7 +131,7 @@ namespace { + auto tree = device_tree(tm, root, device_tree_detail::device_details_traits::ref_counter()); + + try { +- walk_device_tree(tree, de, dv); ++ walk_device_tree(tree, de, dv, true); + } catch (...) { + return optional>(); + } +@@ -156,7 +156,7 @@ namespace { + auto tree = dev_tree(tm, root, mapping_tree_detail::mtree_traits::ref_counter(tm)); + + try { +- walk_mapping_tree(tree, me, mv); ++ walk_mapping_tree(tree, me, mv, true); + } catch (...) { + return optional>(); + } +@@ -722,7 +722,7 @@ namespace { + // Since we're not mutating the btrees we don't need a real space map + noop_map::ptr sm(new noop_map); + single_mapping_tree tree(tm_, subtree_root, mapping_tree_detail::block_time_ref_counter(sm)); +- walk_mapping_tree(tree, dev_id, static_cast(me), *damage_policy_); ++ walk_mapping_tree(tree, dev_id, static_cast(me), *damage_policy_, true); + } + + dump_options const &opts_; +@@ -752,7 +752,7 @@ namespace { + dump_options opts; + details_extractor de(opts); + device_tree_detail::damage_visitor::ptr dd_policy(details_damage_policy(true)); +- walk_device_tree(*md.details_, de, *dd_policy); ++ walk_device_tree(*md.details_, de, *dd_policy, true); + + e->begin_superblock("", sb.time_, + sb.trans_id_, +@@ -765,7 +765,7 @@ namespace { + { + mapping_tree_detail::damage_visitor::ptr md_policy(mapping_damage_policy(true)); + mapping_tree_emit_visitor mte(opts, *md.tm_, e, de.get_details(), mapping_damage_policy(true)); +- walk_mapping_tree(*md.mappings_top_level_, mte, *md_policy); ++ walk_mapping_tree(*md.mappings_top_level_, mte, *md_policy, true); + } + + e->end_superblock(); +@@ -801,7 +801,7 @@ thin_provisioning::metadata_dump(metadata::ptr md, emitter::ptr e, dump_options + { + details_extractor de(opts); + device_tree_detail::damage_visitor::ptr dd_policy(details_damage_policy(false)); +- walk_device_tree(*md->details_, de, *dd_policy); ++ walk_device_tree(*md->details_, de, *dd_policy, true); + + e->begin_superblock("", md->sb_.time_, + md->sb_.trans_id_, +@@ -814,7 +814,7 @@ thin_provisioning::metadata_dump(metadata::ptr md, emitter::ptr e, dump_options + { + mapping_tree_detail::damage_visitor::ptr md_policy(mapping_damage_policy(false)); + mapping_tree_emit_visitor mte(opts, *md->tm_, e, de.get_details(), mapping_damage_policy(false)); +- walk_mapping_tree(*md->mappings_top_level_, mte, *md_policy); ++ walk_mapping_tree(*md->mappings_top_level_, mte, *md_policy, true); + } + + e->end_superblock(); +@@ -844,7 +844,7 @@ thin_provisioning::metadata_dump_subtree(metadata::ptr md, emitter::ptr e, bool + mapping_tree_detail::block_time_ref_counter(md->data_sm_)); + // FIXME: pass the current device id instead of zero + walk_mapping_tree(tree, 0, static_cast(me), +- *mapping_damage_policy(repair)); ++ *mapping_damage_policy(repair), true); + } + + //---------------------------------------------------------------- +diff --git a/thin-provisioning/thin_check.cc b/thin-provisioning/thin_check.cc +index 6373603..c2612b5 100644 +--- a/thin-provisioning/thin_check.cc ++++ b/thin-provisioning/thin_check.cc +@@ -244,7 +244,7 @@ namespace { + nested_output::nest _ = out.push(); + device_tree dtree(*tm, sb.device_details_root_, + device_tree_detail::device_details_traits::ref_counter()); +- check_device_tree(dtree, dev_rep); ++ check_device_tree(dtree, dev_rep, fs.ignore_non_fatal_errors); + } + } + +@@ -254,7 +254,7 @@ namespace { + nested_output::nest _ = out.push(); + dev_tree dtree(*tm, mapping_root(sb, fs), + mapping_tree_detail::mtree_traits::ref_counter(*tm)); +- check_mapping_tree(dtree, mapping_rep); ++ check_mapping_tree(dtree, mapping_rep, fs.ignore_non_fatal_errors); + } + + } else if (fs.check_mapping_tree_level2) { +@@ -263,7 +263,7 @@ namespace { + nested_output::nest _ = out.push(); + mapping_tree mtree(*tm, mapping_root(sb, fs), + mapping_tree_detail::block_traits::ref_counter(tm->get_sm())); +- check_mapping_tree(mtree, mapping_rep); ++ check_mapping_tree(mtree, mapping_rep, fs.ignore_non_fatal_errors); + } + } + diff --git a/SPECS/device-mapper-persistent-data.spec b/SPECS/device-mapper-persistent-data.spec index ecd73ea..3ae2179 100644 --- a/SPECS/device-mapper-persistent-data.spec +++ b/SPECS/device-mapper-persistent-data.spec @@ -5,7 +5,7 @@ Summary: Device-mapper Persistent Data Tools Name: device-mapper-persistent-data Version: 0.8.5 -Release: 3%{?dist} +Release: 3%{?dist}.2 License: GPLv3+ Group: System Environment/Base URL: https://github.com/jthornber/thin-provisioning-tools @@ -16,7 +16,10 @@ Patch1: dmpd-space-map-noop-explicit-option-return.patch # BZ 1528261: Patch2: dmpd-remove-pool-inactive-from-thin_trim-man-page.patch # BZ 1499781: -patch3: dmpd-thin_repair-cache_repair-Check-input-file-exists-earlier.patch +Patch3: dmpd-thin_repair-cache_repair-Check-input-file-exists-earlier.patch +# BZ 1834944: +Patch4: dmpd-thin_check-Under-populated-nodes-are-now-non-fatal.patch +Patch5: dmpd-Count-under-populated-nodes-if-the-option-is-provided.patch BuildRequires: autoconf, expat-devel, libaio-devel, libstdc++-devel, boost-devel Requires: expat @@ -35,6 +38,8 @@ snapshot eras %patch1 -p1 -b .explicit_option_return %patch2 -p1 -b .remove_pool_inactive_option %patch3 -p1 -b .repair_check_input_file_exists_earlier +%patch4 -p1 -b .thin_check_under_populated_nodes_fatal +%patch5 -p1 -b .count_under_populated_nodes echo %{version}-%{release} > VERSION %build @@ -90,6 +95,12 @@ make DESTDIR=%{buildroot} MANDIR=%{_mandir} install %{_sbindir}/thin_trim %changelog +* Mon Sep 21 2020 Marian Csontos - 0.8.5-3.el7_9.2 +- Rebuild for Z stream. + +* Wed Sep 16 2020 Marian Csontos - 0.8.5-3.el7_9.1 +- Underpopulated nodes are considered non fatal error by thin_check. + * Mon Apr 20 2020 Marian Csontos - 0.8.5-3 - Check input file exists earlier.