diff --git a/0126-imsm-Fix-possible-segfault-in-check_no_platform.patch b/0126-imsm-Fix-possible-segfault-in-check_no_platform.patch new file mode 100644 index 0000000..1719f33 --- /dev/null +++ b/0126-imsm-Fix-possible-segfault-in-check_no_platform.patch @@ -0,0 +1,33 @@ +From cf1577bf54afe76b77ecaa62df25901739ca8ae9 Mon Sep 17 00:00:00 2001 +From: Mateusz Grzonka +Date: Wed, 5 Jul 2023 16:34:56 +0200 +Subject: [PATCH 126/165] imsm: Fix possible segfault in check_no_platform() + +conf_line() may return NULL, which is not handled and might cause +segfault. + +Signed-off-by: Mateusz Grzonka +Signed-off-by: Jes Sorensen +--- + super-intel.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/super-intel.c b/super-intel.c +index ae0f4a8c..4ef33d31 100644 +--- a/super-intel.c ++++ b/super-intel.c +@@ -650,6 +650,11 @@ static int check_no_platform(void) + char *l = conf_line(fp); + char *w = l; + ++ if (l == NULL) { ++ fclose(fp); ++ return 0; ++ } ++ + do { + if (strcmp(w, search) == 0) + no_platform = 1; +-- +2.40.1 + diff --git a/0127-imsm-move-sum_extents-calculations-to-merge_extents.patch b/0127-imsm-move-sum_extents-calculations-to-merge_extents.patch new file mode 100644 index 0000000..c5bc8fc --- /dev/null +++ b/0127-imsm-move-sum_extents-calculations-to-merge_extents.patch @@ -0,0 +1,105 @@ +From 9bc426fa1f236b8cad518431574a54fc60718739 Mon Sep 17 00:00:00 2001 +From: Mariusz Tkaczyk +Date: Mon, 29 May 2023 15:52:33 +0200 +Subject: [PATCH 127/165] imsm: move sum_extents calculations to + merge_extents() + +This logic is only used by merge_extents() code, there is no need to pass +it as parameter. Move it up. Add proper description. + +Signed-off-by: Mariusz Tkaczyk +Signed-off-by: Jes Sorensen +--- + super-intel.c | 37 +++++++++++++++++++------------------ + 1 file changed, 19 insertions(+), 18 deletions(-) + +diff --git a/super-intel.c b/super-intel.c +index 4ef33d31..26b20313 100644 +--- a/super-intel.c ++++ b/super-intel.c +@@ -6882,21 +6882,31 @@ static unsigned long long find_size(struct extent *e, int *idx, int num_extents) + return end - base_start; + } + +-static unsigned long long merge_extents(struct intel_super *super, int sum_extents) ++/** merge_extents() - analyze extents and get max common free size. ++ * @super: Intel metadata, not NULL. ++ * ++ * Build a composite disk with all known extents and generate a new maxsize ++ * given the "all disks in an array must share a common start offset" ++ * constraint. ++ * ++ * Return: Max free space or 0 on failure. ++ */ ++static unsigned long long merge_extents(struct intel_super *super) + { +- /* build a composite disk with all known extents and generate a new +- * 'maxsize' given the "all disks in an array must share a common start +- * offset" constraint +- */ +- struct extent *e = xcalloc(sum_extents, sizeof(*e)); ++ struct extent *e; + struct dl *dl; + int i, j; +- int start_extent; ++ int start_extent, sum_extents = 0; + unsigned long long pos; + unsigned long long start = 0; + unsigned long long maxsize; + unsigned long reserve; + ++ for (dl = super->disks; dl; dl = dl->next) ++ if (dl->e) ++ sum_extents += dl->extent_cnt; ++ e = xcalloc(sum_extents, sizeof(struct extent)); ++ + /* coalesce and sort all extents. also, check to see if we need to + * reserve space between member arrays + */ +@@ -7555,13 +7565,7 @@ static int validate_geometry_imsm_volume(struct supertype *st, int level, + return 0; + } + +- /* count total number of extents for merge */ +- i = 0; +- for (dl = super->disks; dl; dl = dl->next) +- if (dl->e) +- i += dl->extent_cnt; +- +- maxsize = merge_extents(super, i); ++ maxsize = merge_extents(super); + + if (mpb->num_raid_devs > 0 && size && size != maxsize) + pr_err("attempting to create a second volume with size less then remaining space.\n"); +@@ -7615,7 +7619,6 @@ static imsm_status_t imsm_get_free_size(struct intel_super *super, + struct imsm_super *mpb = super->anchor; + struct dl *dl; + int i; +- int extent_cnt; + struct extent *e; + unsigned long long maxsize; + unsigned long long minsize; +@@ -7624,7 +7627,6 @@ static imsm_status_t imsm_get_free_size(struct intel_super *super, + + /* find the largest common start free region of the possible disks */ + used = 0; +- extent_cnt = 0; + cnt = 0; + for (dl = super->disks; dl; dl = dl->next) { + dl->raiddisk = -1; +@@ -7645,11 +7647,10 @@ static imsm_status_t imsm_get_free_size(struct intel_super *super, + ; + dl->e = e; + dl->extent_cnt = i; +- extent_cnt += i; + cnt++; + } + +- maxsize = merge_extents(super, extent_cnt); ++ maxsize = merge_extents(super); + minsize = size; + if (size == 0) + /* chunk is in K */ +-- +2.40.1 + diff --git a/0128-imsm-imsm_get_free_size-refactor.patch b/0128-imsm-imsm_get_free_size-refactor.patch new file mode 100644 index 0000000..ed43855 --- /dev/null +++ b/0128-imsm-imsm_get_free_size-refactor.patch @@ -0,0 +1,72 @@ +From 5f027b9357c011ca0421400e258a777d97f18d17 Mon Sep 17 00:00:00 2001 +From: Mariusz Tkaczyk +Date: Mon, 29 May 2023 15:52:34 +0200 +Subject: [PATCH 128/165] imsm: imsm_get_free_size() refactor. + +Move minsize calculations up. Add error message if free size is too small. + +Signed-off-by: Mariusz Tkaczyk +Signed-off-by: Jes Sorensen +--- + super-intel.c | 27 ++++++++++++++------------- + 1 file changed, 14 insertions(+), 13 deletions(-) + +diff --git a/super-intel.c b/super-intel.c +index 26b20313..16a30ba7 100644 +--- a/super-intel.c ++++ b/super-intel.c +@@ -7600,7 +7600,7 @@ static int validate_geometry_imsm_volume(struct supertype *st, int level, + * @super: &intel_super pointer, not NULL. + * @raiddisks: number of raid disks. + * @size: requested size, could be 0 (means max size). +- * @chunk: requested chunk. ++ * @chunk: requested chunk size in KiB. + * @freesize: pointer for returned size value. + * + * Return: &IMSM_STATUS_OK or &IMSM_STATUS_ERROR. +@@ -7620,14 +7620,15 @@ static imsm_status_t imsm_get_free_size(struct intel_super *super, + struct dl *dl; + int i; + struct extent *e; ++ int cnt = 0; ++ int used = 0; + unsigned long long maxsize; +- unsigned long long minsize; +- int cnt; +- int used; ++ unsigned long long minsize = size; ++ ++ if (minsize == 0) ++ minsize = chunk * 2; + + /* find the largest common start free region of the possible disks */ +- used = 0; +- cnt = 0; + for (dl = super->disks; dl; dl = dl->next) { + dl->raiddisk = -1; + +@@ -7651,14 +7652,14 @@ static imsm_status_t imsm_get_free_size(struct intel_super *super, + } + + maxsize = merge_extents(super); +- minsize = size; +- if (size == 0) +- /* chunk is in K */ +- minsize = chunk * 2; ++ if (maxsize < minsize) { ++ pr_err("imsm: Free space is %llu but must be equal or larger than %llu.\n", ++ maxsize, minsize); ++ return IMSM_STATUS_ERROR; ++ } + +- if (cnt < raiddisks || (super->orom && used && used != raiddisks) || +- maxsize < minsize || maxsize == 0) { +- pr_err("not enough devices with space to create array.\n"); ++ if (cnt < raiddisks || (super->orom && used && used != raiddisks)) { ++ pr_err("imsm: Not enough devices with space to create array.\n"); + return IMSM_STATUS_ERROR; + } + +-- +2.40.1 + diff --git a/0129-imsm-introduce-round_member_size_to_mb.patch b/0129-imsm-introduce-round_member_size_to_mb.patch new file mode 100644 index 0000000..50d1d36 --- /dev/null +++ b/0129-imsm-introduce-round_member_size_to_mb.patch @@ -0,0 +1,68 @@ +From 78c8028b331c8e281554d43fde4d46e9cb4a227e Mon Sep 17 00:00:00 2001 +From: Mariusz Tkaczyk +Date: Mon, 29 May 2023 15:52:35 +0200 +Subject: [PATCH 129/165] imsm: introduce round_member_size_to_mb() + +Extract rounding logic to separate function. + +Signed-off-by: Mariusz Tkaczyk +Signed-off-by: Jes Sorensen +--- + super-intel.c | 31 +++++++++++++++++++++---------- + 1 file changed, 21 insertions(+), 10 deletions(-) + +diff --git a/super-intel.c b/super-intel.c +index 16a30ba7..36171107 100644 +--- a/super-intel.c ++++ b/super-intel.c +@@ -1644,17 +1644,29 @@ static int is_journal(struct imsm_disk *disk) + return (disk->status & JOURNAL_DISK) == JOURNAL_DISK; + } + +-/* round array size down to closest MB and ensure it splits evenly +- * between members ++/** ++ * round_member_size_to_mb()- Round given size to closest MiB. ++ * @size: size to round in sectors. + */ +-static unsigned long long round_size_to_mb(unsigned long long size, unsigned int +- disk_count) ++static inline unsigned long long round_member_size_to_mb(unsigned long long size) + { +- size /= disk_count; +- size = (size >> SECT_PER_MB_SHIFT) << SECT_PER_MB_SHIFT; +- size *= disk_count; ++ return (size >> SECT_PER_MB_SHIFT) << SECT_PER_MB_SHIFT; ++} + +- return size; ++/** ++ * round_size_to_mb()- Round given size. ++ * @array_size: size to round in sectors. ++ * @disk_count: count of data members. ++ * ++ * Get size per each data member and round it to closest MiB to ensure that data ++ * splits evenly between members. ++ * ++ * Return: Array size, rounded down. ++ */ ++static inline unsigned long long round_size_to_mb(unsigned long long array_size, ++ unsigned int disk_count) ++{ ++ return round_member_size_to_mb(array_size / disk_count) * disk_count; + } + + static int able_to_resync(int raid_level, int missing_disks) +@@ -11810,8 +11822,7 @@ enum imsm_reshape_type imsm_analyze_change(struct supertype *st, + } else { + /* round size due to metadata compatibility + */ +- geo->size = (geo->size >> SECT_PER_MB_SHIFT) +- << SECT_PER_MB_SHIFT; ++ geo->size = round_member_size_to_mb(geo->size); + dprintf("Prepare update for size change to %llu\n", + geo->size ); + if (current_size >= geo->size) { +-- +2.40.1 + diff --git a/0130-imsm-move-expand-verification-code-into-new-function.patch b/0130-imsm-move-expand-verification-code-into-new-function.patch new file mode 100644 index 0000000..6e5359e --- /dev/null +++ b/0130-imsm-move-expand-verification-code-into-new-function.patch @@ -0,0 +1,238 @@ +From cbaa7904a175628a294ed54b4de6c52afa4f830d Mon Sep 17 00:00:00 2001 +From: Mariusz Tkaczyk +Date: Mon, 29 May 2023 15:52:36 +0200 +Subject: [PATCH 130/165] imsm: move expand verification code into new function + +The code here is too complex. Move it to separate function and +simplify it. Add more error messages. + +Signed-off-by: Mariusz Tkaczyk +Signed-off-by: Jes Sorensen +--- + super-intel.c | 187 +++++++++++++++++++++++++++----------------------- + 1 file changed, 101 insertions(+), 86 deletions(-) + +diff --git a/super-intel.c b/super-intel.c +index 36171107..5bd70356 100644 +--- a/super-intel.c ++++ b/super-intel.c +@@ -11643,6 +11643,102 @@ static void imsm_update_metadata_locally(struct supertype *st, + } + } + ++/** ++ * imsm_analyze_expand() - check expand properties and calculate new size. ++ * @st: imsm supertype. ++ * @geo: new geometry params. ++ * @array: array info. ++ * @direction: reshape direction. ++ * ++ * Obtain free space after the &array and verify if expand to requested size is ++ * possible. If geo->size is set to %MAX_SIZE, assume that max free size is ++ * requested. ++ * ++ * Return: ++ * On success %IMSM_STATUS_OK is returned, geo->size and geo->raid_disks are ++ * updated. ++ * On error, %IMSM_STATUS_ERROR is returned. ++ */ ++static imsm_status_t imsm_analyze_expand(struct supertype *st, ++ struct geo_params *geo, ++ struct mdinfo *array, ++ int direction) ++{ ++ struct intel_super *super = st->sb; ++ struct imsm_dev *dev = get_imsm_dev(super, super->current_vol); ++ struct imsm_map *map = get_imsm_map(dev, MAP_0); ++ int data_disks = imsm_num_data_members(map); ++ ++ unsigned long long current_size; ++ unsigned long long free_size; ++ unsigned long long new_size; ++ unsigned long long max_size; ++ ++ const int chunk_kib = geo->chunksize / 1024; ++ imsm_status_t rv; ++ ++ if (direction == ROLLBACK_METADATA_CHANGES) { ++ /** ++ * Accept size for rollback only. ++ */ ++ new_size = geo->size * 2; ++ goto success; ++ } ++ ++ if (super->current_vol + 1 != super->anchor->num_raid_devs) { ++ pr_err("imsm: The last volume in container can be expanded only (%i/%s).\n", ++ super->current_vol, st->devnm); ++ return IMSM_STATUS_ERROR; ++ } ++ ++ if (data_disks == 0) { ++ pr_err("imsm: Cannot retrieve data disks.\n"); ++ return IMSM_STATUS_ERROR; ++ } ++ current_size = array->custom_array_size / data_disks; ++ ++ rv = imsm_get_free_size(super, dev->vol.map->num_members, 0, chunk_kib, &free_size); ++ if (rv != IMSM_STATUS_OK) { ++ pr_err("imsm: Cannot find free space for expand.\n"); ++ return IMSM_STATUS_ERROR; ++ } ++ max_size = round_member_size_to_mb(free_size + current_size); ++ ++ if (geo->size == MAX_SIZE) ++ new_size = max_size; ++ else ++ new_size = round_member_size_to_mb(geo->size * 2); ++ ++ if (new_size == 0) { ++ pr_err("imsm: Rounded requested size is 0.\n"); ++ return IMSM_STATUS_ERROR; ++ } ++ ++ if (new_size > max_size) { ++ pr_err("imsm: Rounded requested size (%llu) is larger than free space available (%llu).\n", ++ new_size, max_size); ++ return IMSM_STATUS_ERROR; ++ } ++ ++ if (new_size == current_size) { ++ pr_err("imsm: Rounded requested size (%llu) is same as current size (%llu).\n", ++ new_size, current_size); ++ return IMSM_STATUS_ERROR; ++ } ++ ++ if (new_size < current_size) { ++ pr_err("imsm: Size reduction is not supported, rounded requested size (%llu) is smaller than current (%llu).\n", ++ new_size, current_size); ++ return IMSM_STATUS_ERROR; ++ } ++ ++success: ++ dprintf("imsm: New size per member is %llu.\n", new_size); ++ geo->size = data_disks * new_size; ++ geo->raid_disks = dev->vol.map->num_members; ++ return IMSM_STATUS_OK; ++} ++ + /*************************************************************************** + * Function: imsm_analyze_change + * Description: Function analyze change for single volume +@@ -11663,13 +11759,6 @@ enum imsm_reshape_type imsm_analyze_change(struct supertype *st, + int devNumChange = 0; + /* imsm compatible layout value for array geometry verification */ + int imsm_layout = -1; +- int data_disks; +- struct imsm_dev *dev; +- struct imsm_map *map; +- struct intel_super *super; +- unsigned long long current_size; +- unsigned long long free_size; +- unsigned long long max_size; + imsm_status_t rv; + + getinfo_super_imsm_volume(st, &info, NULL); +@@ -11752,94 +11841,20 @@ enum imsm_reshape_type imsm_analyze_change(struct supertype *st, + geo->chunksize = info.array.chunk_size; + } + +- chunk = geo->chunksize / 1024; +- +- super = st->sb; +- dev = get_imsm_dev(super, super->current_vol); +- map = get_imsm_map(dev, MAP_0); +- data_disks = imsm_num_data_members(map); +- /* compute current size per disk member +- */ +- current_size = info.custom_array_size / data_disks; +- +- if (geo->size > 0 && geo->size != MAX_SIZE) { +- /* align component size +- */ +- geo->size = imsm_component_size_alignment_check( +- get_imsm_raid_level(dev->vol.map), +- chunk * 1024, super->sector_size, +- geo->size * 2); +- if (geo->size == 0) { +- pr_err("Error. Size expansion is supported only (current size is %llu, requested size /rounded/ is 0).\n", +- current_size); +- goto analyse_change_exit; +- } +- } +- +- if (current_size != geo->size && geo->size > 0) { ++ if (geo->size > 0) { + if (change != -1) { + pr_err("Error. Size change should be the only one at a time.\n"); + change = -1; + goto analyse_change_exit; + } +- if ((super->current_vol + 1) != super->anchor->num_raid_devs) { +- pr_err("Error. The last volume in container can be expanded only (%i/%s).\n", +- super->current_vol, st->devnm); +- goto analyse_change_exit; +- } +- /* check the maximum available size +- */ +- rv = imsm_get_free_size(super, dev->vol.map->num_members, +- 0, chunk, &free_size); + ++ rv = imsm_analyze_expand(st, geo, &info, direction); + if (rv != IMSM_STATUS_OK) +- /* Cannot find maximum available space +- */ +- max_size = 0; +- else { +- max_size = free_size + current_size; +- /* align component size +- */ +- max_size = imsm_component_size_alignment_check( +- get_imsm_raid_level(dev->vol.map), +- chunk * 1024, super->sector_size, +- max_size); +- } +- if (geo->size == MAX_SIZE) { +- /* requested size change to the maximum available size +- */ +- if (max_size == 0) { +- pr_err("Error. Cannot find maximum available space.\n"); +- change = -1; +- goto analyse_change_exit; +- } else +- geo->size = max_size; +- } +- +- if (direction == ROLLBACK_METADATA_CHANGES) { +- /* accept size for rollback only +- */ +- } else { +- /* round size due to metadata compatibility +- */ +- geo->size = round_member_size_to_mb(geo->size); +- dprintf("Prepare update for size change to %llu\n", +- geo->size ); +- if (current_size >= geo->size) { +- pr_err("Error. Size expansion is supported only (current size is %llu, requested size /rounded/ is %llu).\n", +- current_size, geo->size); +- goto analyse_change_exit; +- } +- if (max_size && geo->size > max_size) { +- pr_err("Error. Requested size is larger than maximum available size (maximum available size is %llu, requested size /rounded/ is %llu).\n", +- max_size, geo->size); +- goto analyse_change_exit; +- } +- } +- geo->size *= data_disks; +- geo->raid_disks = dev->vol.map->num_members; ++ goto analyse_change_exit; + change = CH_ARRAY_SIZE; + } ++ ++ chunk = geo->chunksize / 1024; + if (!validate_geometry_imsm(st, + geo->level, + imsm_layout, +-- +2.40.1 + diff --git a/0131-imsm-return-free-space-after-volume-for-expand.patch b/0131-imsm-return-free-space-after-volume-for-expand.patch new file mode 100644 index 0000000..10faec5 --- /dev/null +++ b/0131-imsm-return-free-space-after-volume-for-expand.patch @@ -0,0 +1,214 @@ +From aa19fdd45b2ba474f6a51a6d4b8f3c44ef19dafd Mon Sep 17 00:00:00 2001 +From: Mariusz Tkaczyk +Date: Mon, 29 May 2023 15:52:37 +0200 +Subject: [PATCH 131/165] imsm: return free space after volume for expand + +merge_extends() routine searches for the biggest free space. For expand, +it works only in standard cases where the last volume is expanded and +the free space is determined after the last volume. +Add volume index to extent struct and use that do determine size after +super->current_vol during expand. + +Limitation to last volume is no longer needed. It unblocks scenarios +where kill-subarray is used to remove first volume and later it is +recreated (now it is the second volume, even if it is placed before +existing one). + +Signed-off-by: Mariusz Tkaczyk +Signed-off-by: Jes Sorensen +--- + super-intel.c | 71 +++++++++++++++++++++++++++------------------------ + 1 file changed, 37 insertions(+), 34 deletions(-) + +diff --git a/super-intel.c b/super-intel.c +index 5bd70356..e249d925 100644 +--- a/super-intel.c ++++ b/super-intel.c +@@ -499,8 +499,15 @@ struct intel_disk { + struct intel_disk *next; + }; + ++/** ++ * struct extent - reserved space details. ++ * @start: start offset. ++ * @size: size of reservation, set to 0 for metadata reservation. ++ * @vol: index of the volume, meaningful if &size is set. ++ */ + struct extent { + unsigned long long start, size; ++ int vol; + }; + + /* definitions of reshape process types */ +@@ -1539,9 +1546,10 @@ static struct extent *get_extents(struct intel_super *super, struct dl *dl, + int get_minimal_reservation) + { + /* find a list of used extents on the given physical device */ +- struct extent *rv, *e; +- int i; + int memberships = count_memberships(dl, super); ++ struct extent *rv = xcalloc(memberships + 1, sizeof(struct extent)); ++ struct extent *e = rv; ++ int i; + __u32 reservation; + + /* trim the reserved area for spares, so they can join any array +@@ -1553,9 +1561,6 @@ static struct extent *get_extents(struct intel_super *super, struct dl *dl, + else + reservation = MPB_SECTOR_CNT + IMSM_RESERVED_SECTORS; + +- rv = xcalloc(sizeof(struct extent), (memberships + 1)); +- e = rv; +- + for (i = 0; i < super->anchor->num_raid_devs; i++) { + struct imsm_dev *dev = get_imsm_dev(super, i); + struct imsm_map *map = get_imsm_map(dev, MAP_0); +@@ -1563,6 +1568,7 @@ static struct extent *get_extents(struct intel_super *super, struct dl *dl, + if (get_imsm_disk_slot(map, dl->index) >= 0) { + e->start = pba_of_lba0(map); + e->size = per_dev_array_size(map); ++ e->vol = i; + e++; + } + } +@@ -6894,24 +6900,26 @@ static unsigned long long find_size(struct extent *e, int *idx, int num_extents) + return end - base_start; + } + +-/** merge_extents() - analyze extents and get max common free size. ++/** merge_extents() - analyze extents and get free size. + * @super: Intel metadata, not NULL. ++ * @expanding: if set, we are expanding &super->current_vol. + * +- * Build a composite disk with all known extents and generate a new maxsize +- * given the "all disks in an array must share a common start offset" +- * constraint. ++ * Build a composite disk with all known extents and generate a size given the ++ * "all disks in an array must share a common start offset" constraint. ++ * If a volume is expanded, then return free space after the volume. + * +- * Return: Max free space or 0 on failure. ++ * Return: Free space or 0 on failure. + */ +-static unsigned long long merge_extents(struct intel_super *super) ++static unsigned long long merge_extents(struct intel_super *super, const bool expanding) + { + struct extent *e; + struct dl *dl; +- int i, j; +- int start_extent, sum_extents = 0; +- unsigned long long pos; ++ int i, j, pos_vol_idx = -1; ++ int extent_idx = 0; ++ int sum_extents = 0; ++ unsigned long long pos = 0; + unsigned long long start = 0; +- unsigned long long maxsize; ++ unsigned long long maxsize = 0; + unsigned long reserve; + + for (dl = super->disks; dl; dl = dl->next) +@@ -6936,26 +6944,26 @@ static unsigned long long merge_extents(struct intel_super *super) + j = 0; + while (i < sum_extents) { + e[j].start = e[i].start; ++ e[j].vol = e[i].vol; + e[j].size = find_size(e, &i, sum_extents); + j++; + if (e[j-1].size == 0) + break; + } + +- pos = 0; +- maxsize = 0; +- start_extent = 0; + i = 0; + do { +- unsigned long long esize; ++ unsigned long long esize = e[i].start - pos; + +- esize = e[i].start - pos; +- if (esize >= maxsize) { ++ if (expanding ? pos_vol_idx == super->current_vol : esize >= maxsize) { + maxsize = esize; + start = pos; +- start_extent = i; ++ extent_idx = i; + } ++ + pos = e[i].start + e[i].size; ++ pos_vol_idx = e[i].vol; ++ + i++; + } while (e[i-1].size); + free(e); +@@ -6966,7 +6974,7 @@ static unsigned long long merge_extents(struct intel_super *super) + /* FIXME assumes volume at offset 0 is the first volume in a + * container + */ +- if (start_extent > 0) ++ if (extent_idx > 0) + reserve = IMSM_RESERVED_SECTORS; /* gap between raid regions */ + else + reserve = 0; +@@ -7577,7 +7585,7 @@ static int validate_geometry_imsm_volume(struct supertype *st, int level, + return 0; + } + +- maxsize = merge_extents(super); ++ maxsize = merge_extents(super, false); + + if (mpb->num_raid_devs > 0 && size && size != maxsize) + pr_err("attempting to create a second volume with size less then remaining space.\n"); +@@ -7626,7 +7634,8 @@ static imsm_status_t imsm_get_free_size(struct intel_super *super, + const int raiddisks, + unsigned long long size, + const int chunk, +- unsigned long long *freesize) ++ unsigned long long *freesize, ++ bool expanding) + { + struct imsm_super *mpb = super->anchor; + struct dl *dl; +@@ -7663,7 +7672,7 @@ static imsm_status_t imsm_get_free_size(struct intel_super *super, + cnt++; + } + +- maxsize = merge_extents(super); ++ maxsize = merge_extents(super, expanding); + if (maxsize < minsize) { + pr_err("imsm: Free space is %llu but must be equal or larger than %llu.\n", + maxsize, minsize); +@@ -7721,7 +7730,7 @@ static imsm_status_t autolayout_imsm(struct intel_super *super, + int vol_cnt = super->anchor->num_raid_devs; + imsm_status_t rv; + +- rv = imsm_get_free_size(super, raiddisks, size, chunk, freesize); ++ rv = imsm_get_free_size(super, raiddisks, size, chunk, freesize, false); + if (rv != IMSM_STATUS_OK) + return IMSM_STATUS_ERROR; + +@@ -11685,19 +11694,13 @@ static imsm_status_t imsm_analyze_expand(struct supertype *st, + goto success; + } + +- if (super->current_vol + 1 != super->anchor->num_raid_devs) { +- pr_err("imsm: The last volume in container can be expanded only (%i/%s).\n", +- super->current_vol, st->devnm); +- return IMSM_STATUS_ERROR; +- } +- + if (data_disks == 0) { + pr_err("imsm: Cannot retrieve data disks.\n"); + return IMSM_STATUS_ERROR; + } + current_size = array->custom_array_size / data_disks; + +- rv = imsm_get_free_size(super, dev->vol.map->num_members, 0, chunk_kib, &free_size); ++ rv = imsm_get_free_size(super, dev->vol.map->num_members, 0, chunk_kib, &free_size, true); + if (rv != IMSM_STATUS_OK) { + pr_err("imsm: Cannot find free space for expand.\n"); + return IMSM_STATUS_ERROR; +-- +2.40.1 + diff --git a/0132-imsm-fix-free-space-calculations.patch b/0132-imsm-fix-free-space-calculations.patch new file mode 100644 index 0000000..fe3025f --- /dev/null +++ b/0132-imsm-fix-free-space-calculations.patch @@ -0,0 +1,113 @@ +From 1dea84ae38288fbefa04d9fda2b3f36c21a9e1bd Mon Sep 17 00:00:00 2001 +From: Mariusz Tkaczyk +Date: Mon, 29 May 2023 15:52:38 +0200 +Subject: [PATCH 132/165] imsm: fix free space calculations + +Between two volumes or between last volume and metadata at least +IMSM_RESERVED_SECTORS gap must exist. Currently the gap can be doubled +because metadata reservation contains IMSM_RESERVED_SECTORS too. + +Divide reserve variable into pre_reservation and post_reservation to be +more flexible and decide separately if each reservation is needed. + +Pre_reservation is needed only when a volume is created and it is not a +real first volume in a container (we can check that by extent_idx). +This type of reservation is not needed for expand. + +Post_reservation is not needed only if real last volume is created or +expanded because reservation is done with the metadata. + +The volume index in metadata cannot be trusted, because the real volume +order can be reversed. It is safer to use extent table, it is sorted by +start position. + +Signed-off-by: Mariusz Tkaczyk +Signed-off-by: Jes Sorensen +--- + super-intel.c | 50 ++++++++++++++++++++++++++++++-------------------- + 1 file changed, 30 insertions(+), 20 deletions(-) + +diff --git a/super-intel.c b/super-intel.c +index e249d925..824c1356 100644 +--- a/super-intel.c ++++ b/super-intel.c +@@ -6919,8 +6919,11 @@ static unsigned long long merge_extents(struct intel_super *super, const bool ex + int sum_extents = 0; + unsigned long long pos = 0; + unsigned long long start = 0; +- unsigned long long maxsize = 0; +- unsigned long reserve; ++ unsigned long long free_size = 0; ++ ++ unsigned long pre_reservation = 0; ++ unsigned long post_reservation = IMSM_RESERVED_SECTORS; ++ unsigned long reservation_size; + + for (dl = super->disks; dl; dl = dl->next) + if (dl->e) +@@ -6955,8 +6958,8 @@ static unsigned long long merge_extents(struct intel_super *super, const bool ex + do { + unsigned long long esize = e[i].start - pos; + +- if (expanding ? pos_vol_idx == super->current_vol : esize >= maxsize) { +- maxsize = esize; ++ if (expanding ? pos_vol_idx == super->current_vol : esize >= free_size) { ++ free_size = esize; + start = pos; + extent_idx = i; + } +@@ -6966,28 +6969,35 @@ static unsigned long long merge_extents(struct intel_super *super, const bool ex + + i++; + } while (e[i-1].size); +- free(e); + +- if (maxsize == 0) ++ if (free_size == 0) { ++ dprintf("imsm: Cannot find free size.\n"); ++ free(e); + return 0; ++ } + +- /* FIXME assumes volume at offset 0 is the first volume in a +- * container +- */ +- if (extent_idx > 0) +- reserve = IMSM_RESERVED_SECTORS; /* gap between raid regions */ +- else +- reserve = 0; ++ if (!expanding && extent_idx != 0) ++ /* ++ * Not a real first volume in a container is created, pre_reservation is needed. ++ */ ++ pre_reservation = IMSM_RESERVED_SECTORS; + +- if (maxsize < reserve) +- return 0; ++ if (e[extent_idx].size == 0) ++ /* ++ * extent_idx points to the metadata, post_reservation is allready done. ++ */ ++ post_reservation = 0; ++ free(e); + +- super->create_offset = ~((unsigned long long) 0); +- if (start + reserve > super->create_offset) +- return 0; /* start overflows create_offset */ +- super->create_offset = start + reserve; ++ reservation_size = pre_reservation + post_reservation; ++ ++ if (free_size < reservation_size) { ++ dprintf("imsm: Reservation size is greater than free space.\n"); ++ return 0; ++ } + +- return maxsize - reserve; ++ super->create_offset = start + pre_reservation; ++ return free_size - reservation_size; + } + + static int is_raid_level_supported(const struct imsm_orom *orom, int level, int raiddisks) +-- +2.40.1 + diff --git a/0133-Add-secure-gethostname-wrapper.patch b/0133-Add-secure-gethostname-wrapper.patch new file mode 100644 index 0000000..19f6c36 --- /dev/null +++ b/0133-Add-secure-gethostname-wrapper.patch @@ -0,0 +1,124 @@ +From 21e622f214a38c048c5689158bc6314a91a46e40 Mon Sep 17 00:00:00 2001 +From: Blazej Kucman +Date: Fri, 16 Jun 2023 21:45:55 +0200 +Subject: [PATCH 133/165] Add secure gethostname() wrapper + +gethostname() func does not ensure null-terminated string +if hostname is longer than buffer length. +For security, a function s_gethostname() has been added +to ensure that "\0" is added to the end of the buffer. +Previously this had to be handled in each place +of the gethostname() call. + +Signed-off-by: Blazej Kucman +Signed-off-by: Jes Sorensen +--- + Monitor.c | 3 +-- + lib.c | 19 +++++++++++++++++++ + mapfile.c | 3 +-- + mdadm.c | 3 +-- + mdadm.h | 1 + + super-ddf.c | 3 +-- + 6 files changed, 24 insertions(+), 8 deletions(-) + +diff --git a/Monitor.c b/Monitor.c +index 66175968..e74a0558 100644 +--- a/Monitor.c ++++ b/Monitor.c +@@ -222,11 +222,10 @@ int Monitor(struct mddev_dev *devlist, + info.dosyslog = dosyslog; + info.test = c->test; + +- if (gethostname(info.hostname, sizeof(info.hostname)) != 0) { ++ if (s_gethostname(info.hostname, sizeof(info.hostname)) != 0) { + pr_err("Cannot get hostname.\n"); + return 1; + } +- info.hostname[sizeof(info.hostname) - 1] = '\0'; + + if (share){ + if (check_one_sharer(c->scan) == 2) +diff --git a/lib.c b/lib.c +index fe5c8d2c..8a4b48e0 100644 +--- a/lib.c ++++ b/lib.c +@@ -585,3 +585,22 @@ int parse_num(int *dest, const char *num) + *dest = temp; + return 0; + } ++ ++/** ++ * s_gethostname() - secure get hostname. Assure null-terminated string. ++ * ++ * @buf: buffer for hostname. ++ * @buf_len: buffer length. ++ * ++ * Return: gethostname() result. ++ */ ++int s_gethostname(char *buf, int buf_len) ++{ ++ assert(buf); ++ ++ int ret = gethostname(buf, buf_len); ++ ++ buf[buf_len - 1] = 0; ++ ++ return ret; ++} +diff --git a/mapfile.c b/mapfile.c +index 34fea179..f1f3ee2c 100644 +--- a/mapfile.c ++++ b/mapfile.c +@@ -363,8 +363,7 @@ void RebuildMap(void) + char *homehost = conf_get_homehost(&require_homehost); + + if (homehost == NULL || strcmp(homehost, "")==0) { +- if (gethostname(sys_hostname, sizeof(sys_hostname)) == 0) { +- sys_hostname[sizeof(sys_hostname)-1] = 0; ++ if (s_gethostname(sys_hostname, sizeof(sys_hostname)) == 0) { + homehost = sys_hostname; + } + } +diff --git a/mdadm.c b/mdadm.c +index 076b45e0..e32598cb 100644 +--- a/mdadm.c ++++ b/mdadm.c +@@ -1340,8 +1340,7 @@ int main(int argc, char *argv[]) + if (c.homehost == NULL && c.require_homehost) + c.homehost = conf_get_homehost(&c.require_homehost); + if (c.homehost == NULL || strcasecmp(c.homehost, "") == 0) { +- if (gethostname(sys_hostname, sizeof(sys_hostname)) == 0) { +- sys_hostname[sizeof(sys_hostname)-1] = 0; ++ if (s_gethostname(sys_hostname, sizeof(sys_hostname)) == 0) { + c.homehost = sys_hostname; + } + } +diff --git a/mdadm.h b/mdadm.h +index 83f2cf7f..f0ceeb78 100644 +--- a/mdadm.h ++++ b/mdadm.h +@@ -1805,6 +1805,7 @@ extern void set_dlm_hooks(void); + extern void sleep_for(unsigned int sec, long nsec, bool wake_after_interrupt); + extern bool is_directory(const char *path); + extern bool is_file(const char *path); ++extern int s_gethostname(char *buf, int buf_len); + + #define _ROUND_UP(val, base) (((val) + (base) - 1) & ~(base - 1)) + #define ROUND_UP(val, base) _ROUND_UP(val, (typeof(val))(base)) +diff --git a/super-ddf.c b/super-ddf.c +index 7213284e..c5242654 100644 +--- a/super-ddf.c ++++ b/super-ddf.c +@@ -2364,8 +2364,7 @@ static int init_super_ddf(struct supertype *st, + * Remaining 16 are serial number.... maybe a hostname would do? + */ + memcpy(ddf->controller.guid, T10, sizeof(T10)); +- gethostname(hostname, sizeof(hostname)); +- hostname[sizeof(hostname) - 1] = 0; ++ s_gethostname(hostname, sizeof(hostname)); + hostlen = strlen(hostname); + memcpy(ddf->controller.guid + 24 - hostlen, hostname, hostlen); + for (i = strlen(T10) ; i+hostlen < 24; i++) +-- +2.40.1 + diff --git a/0134-mdadm-Stop-mdcheck_continue-timer-when-mdcheck_start.patch b/0134-mdadm-Stop-mdcheck_continue-timer-when-mdcheck_start.patch new file mode 100644 index 0000000..abe9ef6 --- /dev/null +++ b/0134-mdadm-Stop-mdcheck_continue-timer-when-mdcheck_start.patch @@ -0,0 +1,41 @@ +From 1ab341e5ce0cb01a1533a2c36e5b69eabf12bf95 Mon Sep 17 00:00:00 2001 +From: Xiao Ni +Date: Fri, 25 Aug 2023 20:55:41 +0800 +Subject: [PATCH 134/165] mdadm: Stop mdcheck_continue timer when mdcheck_start + service can finish check + +mdcheck_continue is triggered by mdcheck_start timer. It's used to +continue check action if the raid is too big and mdcheck_start +service can't finish check action. If mdcheck start can finish check +action, it doesn't need to mdcheck continue service anymore. So stop +it when mdcheck start service can finish check action. + +Signed-off-by: Xiao Ni +Acked-by: Mariusz Tkaczyk +Signed-off-by: Jes Sorensen +--- + misc/mdcheck | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/misc/mdcheck b/misc/mdcheck +index 700c3e25..f87999d3 100644 +--- a/misc/mdcheck ++++ b/misc/mdcheck +@@ -140,7 +140,13 @@ do + echo $a > $fl + any=yes + done +- if [ -z "$any" ]; then exit 0; fi ++ # mdcheck_continue.timer is started by mdcheck_start.timer. ++ # When the check action can be finished in mdcheck_start.service, ++ # it doesn't need mdcheck_continue anymore. ++ if [ -z "$any" ]; then ++ systemctl stop mdcheck_continue.timer ++ exit 0; ++ fi + sleep 120 + done + +-- +2.40.1 + diff --git a/0135-Fix-memory-leak-in-file-Assemble.patch b/0135-Fix-memory-leak-in-file-Assemble.patch new file mode 100644 index 0000000..d7399c9 --- /dev/null +++ b/0135-Fix-memory-leak-in-file-Assemble.patch @@ -0,0 +1,90 @@ +From e9fb93af0f769d147a13e86ab4e5d0aeb935e9fc Mon Sep 17 00:00:00 2001 +From: Guanqin Miao +Date: Mon, 24 Apr 2023 16:06:34 +0800 +Subject: [PATCH 135/165] Fix memory leak in file Assemble + +When we test mdadm with asan, we found some memory leaks in Assemble.c +We fix these memory leaks based on code logic. + +v2: Set st = NULL before jumping to loop + +Signed-off-by: Guanqin Miao +Signed-off-by: Li Xiao Keng +Acked-by: Mariusz Tkaczyk +Signed-off-by: Jes Sorensen +--- + Assemble.c | 14 ++++++++++++-- + 1 file changed, 12 insertions(+), 2 deletions(-) + +diff --git a/Assemble.c b/Assemble.c +index 49804941..61e8cd17 100644 +--- a/Assemble.c ++++ b/Assemble.c +@@ -341,8 +341,10 @@ static int select_devices(struct mddev_dev *devlist, + st->ss->free_super(st); + dev_policy_free(pol); + domain_free(domains); +- if (tst) ++ if (tst) { + tst->ss->free_super(tst); ++ free(tst); ++ } + return -1; + } + +@@ -417,6 +419,7 @@ static int select_devices(struct mddev_dev *devlist, + st->ss->free_super(st); + dev_policy_free(pol); + domain_free(domains); ++ free(st); + return -1; + } + if (c->verbose > 0) +@@ -425,6 +428,8 @@ static int select_devices(struct mddev_dev *devlist, + + /* make sure we finished the loop */ + tmpdev = NULL; ++ free(st); ++ st = NULL; + goto loop; + } else { + content = *contentp; +@@ -533,6 +538,7 @@ static int select_devices(struct mddev_dev *devlist, + st->ss->free_super(st); + dev_policy_free(pol); + domain_free(domains); ++ free(tst); + return -1; + } + tmpdev->used = 1; +@@ -546,8 +552,10 @@ static int select_devices(struct mddev_dev *devlist, + } + dev_policy_free(pol); + pol = NULL; +- if (tst) ++ if (tst) { + tst->ss->free_super(tst); ++ free(tst); ++ } + } + + /* Check if we found some imsm spares but no members */ +@@ -839,6 +847,7 @@ static int load_devices(struct devs *devices, char *devmap, + close(mdfd); + free(devices); + free(devmap); ++ free(best); + *stp = st; + return -1; + } +@@ -1950,6 +1959,7 @@ out: + } else if (mdfd >= 0) + close(mdfd); + ++ free(best); + /* '2' means 'OK, but not started yet' */ + if (rv == -1) { + free(devices); +-- +2.40.1 + diff --git a/0136-Fix-memory-leak-in-file-Kill.patch b/0136-Fix-memory-leak-in-file-Kill.patch new file mode 100644 index 0000000..d4b1a83 --- /dev/null +++ b/0136-Fix-memory-leak-in-file-Kill.patch @@ -0,0 +1,54 @@ +From 8fd0c565b09ba449418d7d604ceba66313246152 Mon Sep 17 00:00:00 2001 +From: Guanqin Miao +Date: Mon, 24 Apr 2023 16:06:35 +0800 +Subject: [PATCH 136/165] Fix memory leak in file Kill + +When we test mdadm with asan, we found some memory leaks in Kill.c +We fix these memory leaks based on code logic. + +Signed-off-by: Guanqin Miao +Signed-off-by: Li Xiao Keng +Acked-by: Mariusz Tkaczyk +Signed-off-by: Jes Sorensen +--- + Kill.c | 9 ++++++++- + 1 file changed, 8 insertions(+), 1 deletion(-) + +diff --git a/Kill.c b/Kill.c +index bfd0efdc..43c9abed 100644 +--- a/Kill.c ++++ b/Kill.c +@@ -41,6 +41,7 @@ int Kill(char *dev, struct supertype *st, int force, int verbose, int noexcl) + * 4 - failed to find a superblock. + */ + ++ bool free_super = false; + int fd, rv = 0; + + if (force) +@@ -52,8 +53,10 @@ int Kill(char *dev, struct supertype *st, int force, int verbose, int noexcl) + dev); + return 2; + } +- if (st == NULL) ++ if (st == NULL) { + st = guess_super(fd); ++ free_super = true; ++ } + if (st == NULL || st->ss->init_super == NULL) { + if (verbose >= 0) + pr_err("Unrecognised md component device - %s\n", dev); +@@ -77,6 +80,10 @@ int Kill(char *dev, struct supertype *st, int force, int verbose, int noexcl) + rv = 0; + } + } ++ if (free_super && st) { ++ st->ss->free_super(st); ++ free(st); ++ } + close(fd); + return rv; + } +-- +2.40.1 + diff --git a/0137-Fix-memory-leak-in-file-Manage.patch b/0137-Fix-memory-leak-in-file-Manage.patch new file mode 100644 index 0000000..214c0e2 --- /dev/null +++ b/0137-Fix-memory-leak-in-file-Manage.patch @@ -0,0 +1,74 @@ +From f6feb3fbb50f48c193e9e4d775a20aa20f7b47b3 Mon Sep 17 00:00:00 2001 +From: Guanqin Miao +Date: Mon, 24 Apr 2023 16:06:36 +0800 +Subject: [PATCH 137/165] Fix memory leak in file Manage + +When we test mdadm with asan, we found some memory leaks in Manage.c +We fix these memory leaks based on code logic. + +v2: Fix free() of uninitialized 'tst' in abort path. + +Signed-off-by: Guanqin Miao +Signed-off-by: Li Xiao Keng +Acked-by: Mariusz Tkaczyk +Signed-off-by: Jes Sorensen +--- + Manage.c | 13 +++++++++++-- + 1 file changed, 11 insertions(+), 2 deletions(-) + +diff --git a/Manage.c b/Manage.c +index f54de7c6..f997b163 100644 +--- a/Manage.c ++++ b/Manage.c +@@ -222,6 +222,7 @@ int Manage_stop(char *devname, int fd, int verbose, int will_retry) + if (verbose >= 0) + pr_err("Cannot get exclusive access to %s:Perhaps a running process, mounted filesystem or active volume group?\n", + devname); ++ sysfs_free(mdi); + return 1; + } + /* If this is an mdmon managed array, just write 'inactive' +@@ -801,8 +802,14 @@ int Manage_add(int fd, int tfd, struct mddev_dev *dv, + rdev, update, devname, + verbose, array); + dev_st->ss->free_super(dev_st); +- if (rv) ++ if (rv) { ++ free(dev_st); + return rv; ++ } ++ } ++ if (dev_st) { ++ dev_st->ss->free_super(dev_st); ++ free(dev_st); + } + } + if (dv->disposition == 'M') { +@@ -1362,7 +1369,7 @@ int Manage_subdevs(char *devname, int fd, + unsigned long long array_size; + struct mddev_dev *dv; + int tfd = -1; +- struct supertype *tst; ++ struct supertype *tst = NULL; + char *subarray = NULL; + int sysfd = -1; + int count = 0; /* number of actions taken */ +@@ -1699,6 +1706,7 @@ int Manage_subdevs(char *devname, int fd, + break; + } + } ++ free(tst); + if (frozen > 0) + sysfs_set_str(&info, NULL, "sync_action","idle"); + if (test && count == 0) +@@ -1706,6 +1714,7 @@ int Manage_subdevs(char *devname, int fd, + return 0; + + abort: ++ free(tst); + if (frozen > 0) + sysfs_set_str(&info, NULL, "sync_action","idle"); + return !test && busy ? 2 : 1; +-- +2.40.1 + diff --git a/0138-Fix-memory-leak-in-file-mdadm.patch b/0138-Fix-memory-leak-in-file-mdadm.patch new file mode 100644 index 0000000..2cb9f7f --- /dev/null +++ b/0138-Fix-memory-leak-in-file-mdadm.patch @@ -0,0 +1,34 @@ +From e62a561ee8b7157a2390eab215dcef6240bd7b03 Mon Sep 17 00:00:00 2001 +From: Guanqin Miao +Date: Mon, 24 Apr 2023 16:06:37 +0800 +Subject: [PATCH 138/165] Fix memory leak in file mdadm + +When we test mdadm with asan, we found some memory leaks in mdadm.c +We fix these memory leaks based on code logic. + +Signed-off-by: Guanqin Miao +Signed-off-by: Li Xiao Keng +Acked-by: Mariusz Tkaczyk +Signed-off-by: Jes Sorensen +--- + mdadm.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/mdadm.c b/mdadm.c +index e32598cb..22d1c53b 100644 +--- a/mdadm.c ++++ b/mdadm.c +@@ -1708,6 +1708,10 @@ int main(int argc, char *argv[]) + autodetect(); + break; + } ++ if (ss) { ++ ss->ss->free_super(ss); ++ free(ss); ++ } + if (locked) + cluster_release_dlmlock(); + close_fd(&mdfd); +-- +2.40.1 + diff --git a/0139-Fix-unsafe-string-functions.patch b/0139-Fix-unsafe-string-functions.patch new file mode 100644 index 0000000..955b1bb --- /dev/null +++ b/0139-Fix-unsafe-string-functions.patch @@ -0,0 +1,116 @@ +From dd5ab40204b1d78ec3bdbcfd5a38a8ffb72bdb50 Mon Sep 17 00:00:00 2001 +From: Kinga Tanska +Date: Thu, 11 May 2023 04:55:12 +0200 +Subject: [PATCH 139/165] Fix unsafe string functions + +Add string length limitations where necessary to +avoid buffer overflows. + +Signed-off-by: Kinga Tanska +Signed-off-by: Jes Sorensen +--- + mdmon.c | 6 +++--- + mdopen.c | 4 ++-- + platform-intel.c | 2 +- + super-intel.c | 6 +++--- + 4 files changed, 9 insertions(+), 9 deletions(-) + +diff --git a/mdmon.c b/mdmon.c +index cef5bbc8..a2038fe6 100644 +--- a/mdmon.c ++++ b/mdmon.c +@@ -240,7 +240,7 @@ static int make_control_sock(char *devname) + return -1; + + addr.sun_family = PF_LOCAL; +- strcpy(addr.sun_path, path); ++ snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", path); + umask(077); /* ensure no world write access */ + if (bind(sfd, (struct sockaddr*)&addr, sizeof(addr)) < 0) { + close(sfd); +@@ -389,7 +389,7 @@ int main(int argc, char *argv[]) + + if (all) { + struct mdstat_ent *mdstat, *e; +- int container_len = strlen(container_name); ++ int container_len = strnlen(container_name, MD_NAME_MAX); + + /* launch an mdmon instance for each container found */ + mdstat = mdstat_read(0, 0); +@@ -472,7 +472,7 @@ static int mdmon(char *devnm, int must_fork, int takeover) + pfd[0] = pfd[1] = -1; + + container = xcalloc(1, sizeof(*container)); +- strcpy(container->devnm, devnm); ++ snprintf(container->devnm, MD_NAME_MAX, "%s", devnm); + container->arrays = NULL; + container->sock = -1; + +diff --git a/mdopen.c b/mdopen.c +index d3022a54..3daa71f9 100644 +--- a/mdopen.c ++++ b/mdopen.c +@@ -193,14 +193,14 @@ int create_mddev(char *dev, char *name, int autof, int trustworthy, + + if (dev) { + if (strncmp(dev, DEV_MD_DIR, DEV_MD_DIR_LEN) == 0) { +- strcpy(cname, dev + DEV_MD_DIR_LEN); ++ snprintf(cname, MD_NAME_MAX, "%s", dev + DEV_MD_DIR_LEN); + } else if (strncmp(dev, "/dev/", 5) == 0) { + char *e = dev + strlen(dev); + while (e > dev && isdigit(e[-1])) + e--; + if (e[0]) + num = strtoul(e, NULL, 10); +- strcpy(cname, dev+5); ++ snprintf(cname, MD_NAME_MAX, "%s", dev + 5); + cname[e-(dev+5)] = 0; + /* name *must* be mdXX or md_dXX in this context */ + if (num < 0 || +diff --git a/platform-intel.c b/platform-intel.c +index 914164c0..eb6e1b7e 100644 +--- a/platform-intel.c ++++ b/platform-intel.c +@@ -214,7 +214,7 @@ struct sys_dev *device_by_id_and_path(__u16 device_id, const char *path) + + static int devpath_to_ll(const char *dev_path, const char *entry, unsigned long long *val) + { +- char path[strlen(dev_path) + strlen(entry) + 2]; ++ char path[strnlen(dev_path, PATH_MAX) + strnlen(entry, PATH_MAX) + 2]; + int fd; + int n; + +diff --git a/super-intel.c b/super-intel.c +index 824c1356..ce813172 100644 +--- a/super-intel.c ++++ b/super-intel.c +@@ -7043,7 +7043,7 @@ active_arrays_by_format(char *name, char* hba, struct md_list **devlist, + int fd = -1; + while (dev && !is_fd_valid(fd)) { + char *path = xmalloc(strlen(dev->name) + strlen("/dev/") + 1); +- num = sprintf(path, "%s%s", "/dev/", dev->name); ++ num = snprintf(path, PATH_MAX, "%s%s", "/dev/", dev->name); + if (num > 0) + fd = open(path, O_RDONLY, 0); + if (num <= 0 || !is_fd_valid(fd)) { +@@ -7935,7 +7935,7 @@ static int kill_subarray_imsm(struct supertype *st, char *subarray_id) + + if (i < current_vol) + continue; +- sprintf(subarray, "%u", i); ++ snprintf(subarray, sizeof(subarray), "%u", i); + if (is_subarray_active(subarray, st->devnm)) { + pr_err("deleting subarray-%d would change the UUID of active subarray-%d, aborting\n", + current_vol, i); +@@ -11308,7 +11308,7 @@ static const char *imsm_get_disk_controller_domain(const char *path) + char *drv=NULL; + struct stat st; + +- strcpy(disk_path, disk_by_path); ++ strncpy(disk_path, disk_by_path, PATH_MAX); + strncat(disk_path, path, PATH_MAX - strlen(disk_path) - 1); + if (stat(disk_path, &st) == 0) { + struct sys_dev* hba; +-- +2.40.1 + diff --git a/0140-platform-intel-limit-guid-length.patch b/0140-platform-intel-limit-guid-length.patch new file mode 100644 index 0000000..8ed03fa --- /dev/null +++ b/0140-platform-intel-limit-guid-length.patch @@ -0,0 +1,55 @@ +From 5ccd457b29809bee442749b5f66ac27ebba4a72d Mon Sep 17 00:00:00 2001 +From: Kinga Tanska +Date: Thu, 11 May 2023 04:55:13 +0200 +Subject: [PATCH 140/165] platform-intel: limit guid length + +Moving GUID_STR_MAX to header to use it as +a length limitation for snprintf function. + +Signed-off-by: Kinga Tanska +Signed-off-by: Jes Sorensen +--- + platform-intel.c | 3 --- + platform-intel.h | 5 ++++- + 2 files changed, 4 insertions(+), 4 deletions(-) + +diff --git a/platform-intel.c b/platform-intel.c +index eb6e1b7e..ef90c3fd 100644 +--- a/platform-intel.c ++++ b/platform-intel.c +@@ -510,9 +510,6 @@ static const struct imsm_orom *find_imsm_hba_orom(struct sys_dev *hba) + return get_orom_by_device_id(hba->dev_id); + } + +-#define GUID_STR_MAX 37 /* according to GUID format: +- * xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" */ +- + #define EFI_GUID(a, b, c, d0, d1, d2, d3, d4, d5, d6, d7) \ + ((struct efi_guid) \ + {{ (a) & 0xff, ((a) >> 8) & 0xff, ((a) >> 16) & 0xff, ((a) >> 24) & 0xff, \ +diff --git a/platform-intel.h b/platform-intel.h +index 2c0f4e39..ba97fb04 100644 +--- a/platform-intel.h ++++ b/platform-intel.h +@@ -19,6 +19,9 @@ + #include + #include + ++/* according to GUID format: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" */ ++#define GUID_STR_MAX 37 ++ + /* The IMSM Capability (IMSM AHCI and ISCU OROM/EFI variable) Version Table definition */ + struct imsm_orom { + __u8 signature[4]; +@@ -229,7 +232,7 @@ extern struct orom_entry *orom_entries; + + static inline char *guid_str(char *buf, struct efi_guid guid) + { +- sprintf(buf, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", ++ snprintf(buf, GUID_STR_MAX, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", + guid.b[3], guid.b[2], guid.b[1], guid.b[0], + guid.b[5], guid.b[4], guid.b[7], guid.b[6], + guid.b[8], guid.b[9], guid.b[10], guid.b[11], +-- +2.40.1 + diff --git a/0141-imsm-Add-reading-vmd-register-for-finding-imsm-capab.patch b/0141-imsm-Add-reading-vmd-register-for-finding-imsm-capab.patch new file mode 100644 index 0000000..be4cc37 --- /dev/null +++ b/0141-imsm-Add-reading-vmd-register-for-finding-imsm-capab.patch @@ -0,0 +1,220 @@ +From 8d1114be8c0a307d251c24078833b029efabc448 Mon Sep 17 00:00:00 2001 +From: Mateusz Grzonka +Date: Wed, 5 Jul 2023 16:23:17 +0200 +Subject: [PATCH 141/165] imsm: Add reading vmd register for finding imsm + capability + +Currently mdadm does not find imsm capability when running inside VM. +This patch adds the possibility to read from vmd register and check for +capability, effectively allowing to use mdadm with imsm inside virtual machines. + +Additionally refactor find_imsm_capability() to make assignments in new +lines. + +Signed-off-by: Mateusz Grzonka +Signed-off-by: Jes Sorensen +--- + platform-intel.c | 114 ++++++++++++++++++++++++++++++++++++++++++++++- + platform-intel.h | 11 ++++- + super-intel.c | 11 +++-- + 3 files changed, 130 insertions(+), 6 deletions(-) + +diff --git a/platform-intel.c b/platform-intel.c +index ef90c3fd..ac282bc5 100644 +--- a/platform-intel.c ++++ b/platform-intel.c +@@ -700,6 +700,106 @@ const struct imsm_orom *find_imsm_nvme(struct sys_dev *hba) + return &nvme_orom->orom; + } + ++#define VMD_REGISTER_OFFSET 0x3FC ++#define VMD_REGISTER_SKU_SHIFT 1 ++#define VMD_REGISTER_SKU_MASK (0x00000007) ++#define VMD_REGISTER_SKU_PREMIUM 2 ++#define MD_REGISTER_VER_MAJOR_SHIFT 4 ++#define MD_REGISTER_VER_MAJOR_MASK (0x0000000F) ++#define MD_REGISTER_VER_MINOR_SHIFT 8 ++#define MD_REGISTER_VER_MINOR_MASK (0x0000000F) ++ ++/* ++ * read_vmd_register() - Reads VMD register and writes contents to buff ptr ++ * @buff: buffer for vmd register data, should be the size of uint32_t ++ * ++ * Return: 0 on success, 1 on error ++ */ ++int read_vmd_register(uint32_t *buff, struct sys_dev *hba) ++{ ++ int fd; ++ char vmd_pci_config_path[PATH_MAX]; ++ ++ if (!vmd_domain_to_controller(hba, vmd_pci_config_path)) ++ return 1; ++ ++ strncat(vmd_pci_config_path, "/config", PATH_MAX - strnlen(vmd_pci_config_path, PATH_MAX)); ++ ++ fd = open(vmd_pci_config_path, O_RDONLY); ++ if (fd < 0) ++ return 1; ++ ++ if (pread(fd, buff, sizeof(uint32_t), VMD_REGISTER_OFFSET) != sizeof(uint32_t)) { ++ close(fd); ++ return 1; ++ } ++ close(fd); ++ return 0; ++} ++ ++/* ++ * add_vmd_orom() - Adds VMD orom cap to orom list, writes orom_entry ptr into vmd_orom ++ * @vmd_orom: pointer to orom entry pointer ++ * ++ * Return: 0 on success, 1 on error ++ */ ++int add_vmd_orom(struct orom_entry **vmd_orom, struct sys_dev *hba) ++{ ++ uint8_t sku; ++ uint32_t vmd_register_data; ++ struct imsm_orom vmd_orom_cap = { ++ .signature = IMSM_VMD_OROM_COMPAT_SIGNATURE, ++ .sss = IMSM_OROM_SSS_4kB | IMSM_OROM_SSS_8kB | ++ IMSM_OROM_SSS_16kB | IMSM_OROM_SSS_32kB | ++ IMSM_OROM_SSS_64kB | IMSM_OROM_SSS_128kB, ++ .dpa = IMSM_OROM_DISKS_PER_ARRAY_NVME, ++ .tds = IMSM_OROM_TOTAL_DISKS_VMD, ++ .vpa = IMSM_OROM_VOLUMES_PER_ARRAY, ++ .vphba = IMSM_OROM_VOLUMES_PER_HBA_VMD, ++ .attr = IMSM_OROM_ATTR_2TB | IMSM_OROM_ATTR_2TB_DISK, ++ .driver_features = IMSM_OROM_CAPABILITIES_EnterpriseSystem | ++ IMSM_OROM_CAPABILITIES_TPV ++ }; ++ ++ if (read_vmd_register(&vmd_register_data, hba) != 0) ++ return 1; ++ ++ sku = (uint8_t)((vmd_register_data >> VMD_REGISTER_SKU_SHIFT) & ++ VMD_REGISTER_SKU_MASK); ++ ++ if (sku == VMD_REGISTER_SKU_PREMIUM) ++ vmd_orom_cap.rlc = IMSM_OROM_RLC_RAID0 | IMSM_OROM_RLC_RAID1 | ++ IMSM_OROM_RLC_RAID10 | IMSM_OROM_RLC_RAID5; ++ else ++ vmd_orom_cap.rlc = IMSM_OROM_RLC_RAID_CNG; ++ ++ vmd_orom_cap.major_ver = (uint8_t) ++ ((vmd_register_data >> MD_REGISTER_VER_MAJOR_SHIFT) & ++ MD_REGISTER_VER_MAJOR_MASK); ++ vmd_orom_cap.minor_ver = (uint8_t) ++ ((vmd_register_data >> MD_REGISTER_VER_MINOR_SHIFT) & ++ MD_REGISTER_VER_MINOR_MASK); ++ ++ *vmd_orom = add_orom(&vmd_orom_cap); ++ ++ return 0; ++} ++ ++const struct imsm_orom *find_imsm_vmd(struct sys_dev *hba) ++{ ++ static struct orom_entry *vmd_orom; ++ ++ if (hba->type != SYS_DEV_VMD) ++ return NULL; ++ ++ if (!vmd_orom && add_vmd_orom(&vmd_orom, hba) != 0) ++ return NULL; ++ ++ add_orom_device_id(vmd_orom, hba->dev_id); ++ vmd_orom->type = SYS_DEV_VMD; ++ return &vmd_orom->orom; ++} ++ + const struct imsm_orom *find_imsm_capability(struct sys_dev *hba) + { + const struct imsm_orom *cap = get_orom_by_device_id(hba->dev_id); +@@ -709,9 +809,19 @@ const struct imsm_orom *find_imsm_capability(struct sys_dev *hba) + + if (hba->type == SYS_DEV_NVME) + return find_imsm_nvme(hba); +- if ((cap = find_imsm_efi(hba)) != NULL) ++ ++ cap = find_imsm_efi(hba); ++ if (cap) + return cap; +- if ((cap = find_imsm_hba_orom(hba)) != NULL) ++ ++ if (hba->type == SYS_DEV_VMD) { ++ cap = find_imsm_vmd(hba); ++ if (cap) ++ return cap; ++ } ++ ++ cap = find_imsm_hba_orom(hba); ++ if (cap) + return cap; + + return NULL; +diff --git a/platform-intel.h b/platform-intel.h +index ba97fb04..ce29d3da 100644 +--- a/platform-intel.h ++++ b/platform-intel.h +@@ -27,6 +27,7 @@ struct imsm_orom { + __u8 signature[4]; + #define IMSM_OROM_SIGNATURE "$VER" + #define IMSM_NVME_OROM_COMPAT_SIGNATURE "$NVM" ++ #define IMSM_VMD_OROM_COMPAT_SIGNATURE "$VMD" + __u8 table_ver_major; /* Currently 2 (can change with future revs) */ + __u8 table_ver_minor; /* Currently 2 (can change with future revs) */ + __u16 major_ver; /* Example: 8 as in 8.6.0.1020 */ +@@ -68,11 +69,13 @@ struct imsm_orom { + __u16 tds; /* Total Disks Supported */ + #define IMSM_OROM_TOTAL_DISKS 6 + #define IMSM_OROM_TOTAL_DISKS_NVME 12 ++ #define IMSM_OROM_TOTAL_DISKS_VMD 48 + __u8 vpa; /* # Volumes Per Array supported */ + #define IMSM_OROM_VOLUMES_PER_ARRAY 2 + __u8 vphba; /* # Volumes Per Host Bus Adapter supported */ + #define IMSM_OROM_VOLUMES_PER_HBA 4 + #define IMSM_OROM_VOLUMES_PER_HBA_NVME 4 ++ #define IMSM_OROM_VOLUMES_PER_HBA_VMD 24 + /* Attributes supported. This should map to the + * attributes in the MPB. Also, lower 16 bits + * should match/duplicate RLC bits above. +@@ -185,7 +188,13 @@ static inline int imsm_orom_is_enterprise(const struct imsm_orom *orom) + static inline int imsm_orom_is_nvme(const struct imsm_orom *orom) + { + return memcmp(orom->signature, IMSM_NVME_OROM_COMPAT_SIGNATURE, +- sizeof(orom->signature)) == 0; ++ sizeof(orom->signature)) == 0; ++} ++ ++static inline int imsm_orom_is_vmd_without_efi(const struct imsm_orom *orom) ++{ ++ return memcmp(orom->signature, IMSM_VMD_OROM_COMPAT_SIGNATURE, ++ sizeof(orom->signature)) == 0; + } + + static inline int imsm_orom_has_tpv_support(const struct imsm_orom *orom) +diff --git a/super-intel.c b/super-intel.c +index ce813172..77b0066f 100644 +--- a/super-intel.c ++++ b/super-intel.c +@@ -2672,9 +2672,14 @@ static void print_imsm_capability(const struct imsm_orom *orom) + else + printf("Rapid Storage Technology%s\n", + imsm_orom_is_enterprise(orom) ? " enterprise" : ""); +- if (orom->major_ver || orom->minor_ver || orom->hotfix_ver || orom->build) +- printf(" Version : %d.%d.%d.%d\n", orom->major_ver, +- orom->minor_ver, orom->hotfix_ver, orom->build); ++ if (orom->major_ver || orom->minor_ver || orom->hotfix_ver || orom->build) { ++ if (imsm_orom_is_vmd_without_efi(orom)) ++ printf(" Version : %d.%d\n", orom->major_ver, ++ orom->minor_ver); ++ else ++ printf(" Version : %d.%d.%d.%d\n", orom->major_ver, ++ orom->minor_ver, orom->hotfix_ver, orom->build); ++ } + printf(" RAID Levels :%s%s%s%s%s\n", + imsm_orom_has_raid0(orom) ? " raid0" : "", + imsm_orom_has_raid1(orom) ? " raid1" : "", +-- +2.40.1 + diff --git a/0142-Add-compiler-defenses-flags.patch b/0142-Add-compiler-defenses-flags.patch new file mode 100644 index 0000000..6102288 --- /dev/null +++ b/0142-Add-compiler-defenses-flags.patch @@ -0,0 +1,109 @@ +From 55a1150c7438afcb7756fccd49713ede20a58e4a Mon Sep 17 00:00:00 2001 +From: Mateusz Grzonka +Date: Mon, 17 Jul 2023 15:19:10 +0200 +Subject: [PATCH 142/165] Add compiler defenses flags + +It is essential to avoid buffer overflows and similar bugs as much as +possible. + +According to Intel rules we are obligated to verify certain +compiler flags, so it will be much easier if they are added to the +Makefile. + +Add gcc flags for prevention of buffer overflows, format string vulnerabilities, +stack protection to prevent stack overwrites and aslr enablement through -fPIE. +Also make the flags configurable. + +The changes were verified on gcc versions 7.5, 8.3, 9.2, 10 and 12.2. + +Signed-off-by: Mateusz Grzonka +Signed-off-by: Jes Sorensen +--- + Makefile | 41 +++++++++++++++++++++++++++++------------ + 1 file changed, 29 insertions(+), 12 deletions(-) + +diff --git a/Makefile b/Makefile +index 5eac1a4e..b3aa36f6 100644 +--- a/Makefile ++++ b/Makefile +@@ -30,7 +30,7 @@ + + # define "CXFLAGS" to give extra flags to CC. + # e.g. make CXFLAGS=-O to optimise +-CXFLAGS ?=-O2 ++CXFLAGS ?=-O2 -D_FORTIFY_SOURCE=2 + TCC = tcc + UCLIBC_GCC = $(shell for nm in i386-uclibc-linux-gcc i386-uclibc-gcc; do which $$nm > /dev/null && { echo $$nm ; exit; } ; done; echo false No uclibc found ) + #DIET_GCC = diet gcc +@@ -50,14 +50,30 @@ ifeq ($(origin CC),default) + CC := $(CROSS_COMPILE)gcc + endif + CXFLAGS ?= -ggdb +-CWFLAGS = -Wall -Werror -Wstrict-prototypes -Wextra -Wno-unused-parameter ++CWFLAGS ?= -Wall -Werror -Wstrict-prototypes -Wextra -Wno-unused-parameter -Wformat -Wformat-security -Werror=format-security -fstack-protector-strong -fPIE -Warray-bounds + ifdef WARN_UNUSED +-CWFLAGS += -Wp,-D_FORTIFY_SOURCE=2 -O3 ++CWFLAGS += -Wp -O3 + endif + +-FALLTHROUGH := $(shell gcc -v --help 2>&1 | grep "implicit-fallthrough" | wc -l) +-ifneq "$(FALLTHROUGH)" "0" +-CWFLAGS += -Wimplicit-fallthrough=0 ++ifeq ($(origin FALLTHROUGH), undefined) ++ FALLTHROUGH := $(shell gcc -Q --help=warnings 2>&1 | grep "implicit-fallthrough" | wc -l) ++ ifneq "$(FALLTHROUGH)" "0" ++ CWFLAGS += -Wimplicit-fallthrough=0 ++ endif ++endif ++ ++ifeq ($(origin FORMATOVERFLOW), undefined) ++ FORMATOVERFLOW := $(shell gcc -Q --help=warnings 2>&1 | grep "format-overflow" | wc -l) ++ ifneq "$(FORMATOVERFLOW)" "0" ++ CWFLAGS += -Wformat-overflow ++ endif ++endif ++ ++ifeq ($(origin STRINGOPOVERFLOW), undefined) ++ STRINGOPOVERFLOW := $(shell gcc -Q --help=warnings 2>&1 | grep "stringop-overflow" | wc -l) ++ ifneq "$(STRINGOPOVERFLOW)" "0" ++ CWFLAGS += -Wstringop-overflow ++ endif + endif + + ifdef DEBIAN +@@ -116,10 +132,12 @@ CFLAGS += -DUSE_PTHREADS + MON_LDFLAGS += -pthread + endif + ++LDFLAGS = -Wl,-z,now,-z,noexecstack ++ + # If you want a static binary, you might uncomment these +-# LDFLAGS = -static ++# LDFLAGS += -static + # STRIP = -s +-LDLIBS = -ldl ++LDLIBS = -ldl -pie + + # To explicitly disable libudev, set -DNO_LIBUDEV in CXFLAGS + ifeq (, $(findstring -DNO_LIBUDEV, $(CXFLAGS))) +@@ -209,14 +227,13 @@ mdadm.Os : $(SRCS) $(INCL) + $(CC) -o mdadm.Os $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -DHAVE_STDINT_H -Os $(SRCS) $(LDLIBS) + + mdadm.O2 : $(SRCS) $(INCL) mdmon.O2 +- $(CC) -o mdadm.O2 $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -DHAVE_STDINT_H -O2 -D_FORTIFY_SOURCE=2 $(SRCS) $(LDLIBS) ++ $(CC) -o mdadm.O2 $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -DHAVE_STDINT_H -O2 $(SRCS) $(LDLIBS) + + mdmon.O2 : $(MON_SRCS) $(INCL) mdmon.h +- $(CC) -o mdmon.O2 $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $(MON_LDFLAGS) -DHAVE_STDINT_H -O2 -D_FORTIFY_SOURCE=2 $(MON_SRCS) $(LDLIBS) ++ $(CC) -o mdmon.O2 $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $(MON_LDFLAGS) -DHAVE_STDINT_H -O2 $(MON_SRCS) $(LDLIBS) + +-# use '-z now' to guarantee no dynamic linker interactions with the monitor thread + mdmon : $(MON_OBJS) | check_rundir +- $(CC) $(CFLAGS) $(LDFLAGS) $(MON_LDFLAGS) -Wl,-z,now -o mdmon $(MON_OBJS) $(LDLIBS) ++ $(CC) $(CFLAGS) $(LDFLAGS) $(MON_LDFLAGS) -o mdmon $(MON_OBJS) $(LDLIBS) + msg.o: msg.c msg.h + + test_stripe : restripe.c xmalloc.o mdadm.h +-- +2.40.1 + diff --git a/0143-Assemble-fix-redundant-memory-free.patch b/0143-Assemble-fix-redundant-memory-free.patch new file mode 100644 index 0000000..4583718 --- /dev/null +++ b/0143-Assemble-fix-redundant-memory-free.patch @@ -0,0 +1,34 @@ +From 024d652e16dd9e3bd1ecdfce4d6f7a8cb498ba42 Mon Sep 17 00:00:00 2001 +From: Kinga Tanska +Date: Tue, 12 Sep 2023 04:27:01 +0200 +Subject: [PATCH 143/165] Assemble: fix redundant memory free + +Commit e9fb93af0f76 ("Fix memory leak in file Assemble") +fixes few memory leaks in Assemble, but it introduces +problem with assembling RAID volume. It was caused by +clearing metadata too fast, not only on fail in +select_devices() function. +This commit removes redundant memory free. + +Signed-off-by: Kinga Tanska +Signed-off-by: Jes Sorensen +--- + Assemble.c | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/Assemble.c b/Assemble.c +index 61e8cd17..5be58e40 100644 +--- a/Assemble.c ++++ b/Assemble.c +@@ -428,8 +428,6 @@ static int select_devices(struct mddev_dev *devlist, + + /* make sure we finished the loop */ + tmpdev = NULL; +- free(st); +- st = NULL; + goto loop; + } else { + content = *contentp; +-- +2.40.1 + diff --git a/0144-tests-add-a-new-test-for-rdev-lifetime.patch b/0144-tests-add-a-new-test-for-rdev-lifetime.patch new file mode 100644 index 0000000..dbbd3c5 --- /dev/null +++ b/0144-tests-add-a-new-test-for-rdev-lifetime.patch @@ -0,0 +1,92 @@ +From 7fe21767d3ab65a686518d2e36d18a07f535972e Mon Sep 17 00:00:00 2001 +From: Yu Kuai +Date: Mon, 29 May 2023 21:28:19 +0800 +Subject: [PATCH 144/165] tests: add a new test for rdev lifetime + +This test add and remove a underlying disk to raid concurretly, verify +that the following problem is fixed: + +run mdadm test 23rdev-lifetime at Fri Apr 28 03:25:30 UTC 2023 +md: could not open device unknown-block(1,0). +sysfs: cannot create duplicate filename '/devices/virtual/block/md0/md/dev-ram0' +CPU: 26 PID: 10521 Comm: test Not tainted 6.3.0-rc2-00134-g7b3a8828043c #115 +Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.16.1-2.fc37 04/014 +Call Trace: + + dump_stack_lvl+0xe7/0x180 + dump_stack+0x18/0x30 + sysfs_warn_dup+0xa2/0xd0 + sysfs_create_dir_ns+0x119/0x140 + kobject_add_internal+0x143/0x4d0 + kobject_add_varg+0x35/0x70 + kobject_add+0x64/0xd0 + bind_rdev_to_array+0x254/0x840 [md_mod] + new_dev_store+0x14d/0x350 [md_mod] + md_attr_store+0xc1/0x1a0 [md_mod] + sysfs_kf_write+0x51/0x70 + kernfs_fop_write_iter+0x188/0x270 + vfs_write+0x27e/0x460 + ksys_write+0x85/0x180 + __x64_sys_write+0x21/0x30 + do_syscall_64+0x6c/0xe0 + entry_SYSCALL_64_after_hwframe+0x63/0xcd +RIP: 0033:0x7f26bacf5387 +Code: 0d 00 f7 d8 64 89 02 48 c7 c0 ff ff ff ff eb b7 0f 1f 00 f3 0f 1e fa 64 84 +RSP: 002b:00007ffe98d79e68 EFLAGS: 00000246 ORIG_RAX: 0000000000000001 +RAX: ffffffffffffffda RBX: 0000000000000004 RCX: 00007f26bacf5387 +RDX: 0000000000000004 RSI: 000055bd10282bf0 RDI: 0000000000000001 +RBP: 000055bd10282bf0 R08: 000000000000000a R09: 00007f26bad8b4e0 +R10: 00007f26bad8b3e0 R11: 0000000000000246 R12: 0000000000000004 +R13: 00007f26badc8520 R14: 0000000000000004 R15: 00007f26badc8700 + + +Signed-off-by: Yu Kuai +Signed-off-by: Jes Sorensen +--- + tests/23rdev-lifetime | 34 ++++++++++++++++++++++++++++++++++ + 1 file changed, 34 insertions(+) + create mode 100644 tests/23rdev-lifetime + +diff --git a/tests/23rdev-lifetime b/tests/23rdev-lifetime +new file mode 100644 +index 00000000..1750b0db +--- /dev/null ++++ b/tests/23rdev-lifetime +@@ -0,0 +1,34 @@ ++devname=${dev0##*/} ++devt=`cat /sys/block/$devname/dev` ++pid="" ++runtime=2 ++ ++clean_up_test() { ++ pill -9 $pid ++ echo clear > /sys/block/md0/md/array_state ++} ++ ++trap 'clean_up_test' EXIT ++ ++add_by_sysfs() { ++ while true; do ++ echo $devt > /sys/block/md0/md/new_dev ++ done ++} ++ ++remove_by_sysfs(){ ++ while true; do ++ echo remove > /sys/block/md0/md/dev-${devname}/state ++ done ++} ++ ++echo md0 > /sys/module/md_mod/parameters/new_array || die "create md0 failed" ++ ++add_by_sysfs & ++pid="$pid $!" ++ ++remove_by_sysfs & ++pid="$pid $!" ++ ++sleep $runtime ++exit 0 +-- +2.40.1 + diff --git a/0145-tests-support-to-skip-checking-dmesg.patch b/0145-tests-support-to-skip-checking-dmesg.patch new file mode 100644 index 0000000..39b38d2 --- /dev/null +++ b/0145-tests-support-to-skip-checking-dmesg.patch @@ -0,0 +1,38 @@ +From 0ef9465f6355db612e53afc32e3084721c3dd7c7 Mon Sep 17 00:00:00 2001 +From: Yu Kuai +Date: Mon, 29 May 2023 21:28:20 +0800 +Subject: [PATCH 145/165] tests: support to skip checking dmesg + +Prepare to add a regression test for raid10 that require error injection +to trigger error path, and kernel will complain about io error, checking +dmesg for error log will make it impossible to pass this test. + +Signed-off-by: Yu Kuai +Acked-by: Mariusz Tkaczyk +Signed-off-by: Jes Sorensen +--- + test | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/test b/test +index 61d9ee83..b244453b 100755 +--- a/test ++++ b/test +@@ -107,8 +107,12 @@ do_test() { + echo -ne "$_script... " + if ( set -ex ; . $_script ) &> $targetdir/log + then +- dmesg | grep -iq "error\|call trace\|segfault" && +- die "dmesg prints errors when testing $_basename!" ++ if [ -f "${_script}.inject_error" ]; then ++ echo "dmesg checking is skipped because test inject error" ++ else ++ dmesg | grep -iq "error\|call trace\|segfault" && ++ die "dmesg prints errors when testing $_basename!" ++ fi + echo "succeeded" + _fail=0 + else +-- +2.40.1 + diff --git a/0146-tests-add-a-regression-test-for-raid10-deadlock.patch b/0146-tests-add-a-regression-test-for-raid10-deadlock.patch new file mode 100644 index 0000000..04e4304 --- /dev/null +++ b/0146-tests-add-a-regression-test-for-raid10-deadlock.patch @@ -0,0 +1,122 @@ +From 3973f65293ce69ddd7c19be9a7a796abfe80e370 Mon Sep 17 00:00:00 2001 +From: Yu Kuai +Date: Mon, 29 May 2023 21:28:21 +0800 +Subject: [PATCH 146/165] tests: add a regression test for raid10 deadlock + +The deadlock is described in [1], it's fixed first by [2], however, +it turns out this commit will trigger other problems[3], hence this +commit will be reverted and the deadlock is supposed to be fixed by [1]. + +[1] https://lore.kernel.org/linux-raid/20230322064122.2384589-5-yukuai1@huaweicloud.com/ +[2] https://lore.kernel.org/linux-raid/20220621031129.24778-1-guoqing.jiang@linux.dev/ +[3] https://lore.kernel.org/linux-raid/20230322064122.2384589-2-yukuai1@huaweicloud.com/ + +Signed-off-by: Yu Kuai +Signed-off-by: Jes Sorensen +--- + tests/24raid10deadlock | 88 +++++++++++++++++++++++++++++ + tests/24raid10deadlock.inject_error | 0 + 2 files changed, 88 insertions(+) + create mode 100644 tests/24raid10deadlock + create mode 100644 tests/24raid10deadlock.inject_error + +diff --git a/tests/24raid10deadlock b/tests/24raid10deadlock +new file mode 100644 +index 00000000..ee330aa9 +--- /dev/null ++++ b/tests/24raid10deadlock +@@ -0,0 +1,88 @@ ++devs="$dev0 $dev1 $dev2 $dev3" ++runtime=120 ++pid="" ++action_pid="" ++ ++set_up_injection() ++{ ++ echo -1 > /sys/kernel/debug/fail_make_request/times ++ echo 1 > /sys/kernel/debug/fail_make_request/probability ++ echo 0 > /sys/kernel/debug/fail_make_request/verbose ++ echo 1 > /sys/block/${1##*/}/make-it-fail ++} ++ ++clean_up_injection() ++{ ++ echo 0 > /sys/block/${1##*/}/make-it-fail ++ echo 0 > /sys/kernel/debug/fail_make_request/times ++ echo 0 > /sys/kernel/debug/fail_make_request/probability ++ echo 2 > /sys/kernel/debug/fail_make_request/verbose ++} ++ ++test_rdev() ++{ ++ while true; do ++ mdadm -f $md0 $1 &> /dev/null ++ mdadm -r $md0 $1 &> /dev/null ++ mdadm --zero-superblock $1 &> /dev/null ++ mdadm -a $md0 $1 &> /dev/null ++ sleep $2 ++ done ++} ++ ++test_write_action() ++{ ++ while true; do ++ echo frozen > /sys/block/md0/md/sync_action ++ echo idle > /sys/block/md0/md/sync_action ++ sleep 0.1 ++ done ++} ++ ++set_up_test() ++{ ++ fio -h &> /dev/null || die "fio not found" ++ ++ # create a simple raid10 ++ mdadm -Cv -R -n 4 -l10 $md0 $devs || die "create raid10 failed" ++} ++ ++clean_up_test() ++{ ++ clean_up_injection $dev0 ++ pkill -9 fio ++ kill -9 $pid ++ kill -9 $action_pid ++ ++ sleep 1 ++ ++ if ps $action_pid | tail -1 | awk '{print $3}' | grep D; then ++ die "thread that is writing sysfs is stuck in D state, deadlock is triggered" ++ fi ++ mdadm -S $md0 ++} ++ ++cat /sys/kernel/debug/fail_make_request/times || die "fault injection is not enabled" ++ ++trap 'clean_up_test' EXIT ++ ++set_up_test || die "set up test failed" ++ ++# backgroup io pressure ++fio -filename=$md0 -rw=randwrite -direct=1 -name=test -bs=4k -numjobs=16 -iodepth=16 & ++ ++# trigger add/remove device by io failure ++set_up_injection $dev0 ++test_rdev $dev0 2 & ++pid="$pid $!" ++ ++# add/remove device directly ++test_rdev $dev3 10 & ++pid="$pid $!" ++ ++test_write_action & ++action_pid="$!" ++ ++sleep $runtime ++ ++exit 0 +diff --git a/tests/24raid10deadlock.inject_error b/tests/24raid10deadlock.inject_error +new file mode 100644 +index 00000000..e69de29b +-- +2.40.1 + diff --git a/0147-tests-add-a-regression-test-for-raid456-deadlock.patch b/0147-tests-add-a-regression-test-for-raid456-deadlock.patch new file mode 100644 index 0000000..78c5836 --- /dev/null +++ b/0147-tests-add-a-regression-test-for-raid456-deadlock.patch @@ -0,0 +1,87 @@ +From 9d0fb58f3b1314678564b4ac7bfb19942acc8926 Mon Sep 17 00:00:00 2001 +From: Yu Kuai +Date: Mon, 29 May 2023 21:28:22 +0800 +Subject: [PATCH 147/165] tests: add a regression test for raid456 deadlock + +The deadlock is described in [1], as the last patch described, it's +fixed first by [2], however this fix will be reverted and the deadlock +is supposed to be fixed by [3]. + +[1] https://lore.kernel.org/linux-raid/5ed54ffc-ce82-bf66-4eff-390cb23bc1ac@molgen.mpg.de/T/#t +[2] https://lore.kernel.org/linux-raid/20220621031129.24778-1-guoqing.jiang@linux.dev/ +[3] https://lore.kernel.org/linux-raid/20230322064122.2384589-5-yukuai1@huaweicloud.com/ + +Signed-off-by: Yu Kuai +Signed-off-by: Jes Sorensen +--- + tests/24raid456deadlock | 58 +++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 58 insertions(+) + create mode 100644 tests/24raid456deadlock + +diff --git a/tests/24raid456deadlock b/tests/24raid456deadlock +new file mode 100644 +index 00000000..80e6e97e +--- /dev/null ++++ b/tests/24raid456deadlock +@@ -0,0 +1,58 @@ ++devs="$dev0 $dev1 $dev2 $dev3 $dev4 $dev5" ++runtime=120 ++pid="" ++old=`cat /proc/sys/vm/dirty_background_ratio` ++ ++test_write_action() ++{ ++ while true; do ++ echo check > /sys/block/md0/md/sync_action &> /dev/null ++ sleep 0.1 ++ echo idle > /sys/block/md0/md/sync_action &> /dev/null ++ done ++} ++ ++test_write_back() ++{ ++ fio -filename=$md0 -bs=4k -rw=write -numjobs=1 -name=test \ ++ -time_based -runtime=$runtime &> /dev/null ++} ++ ++set_up_test() ++{ ++ fio -h &> /dev/null || die "fio not found" ++ ++ # create a simple raid6 ++ mdadm -Cv -R -n 6 -l6 $md0 $devs --assume-clean || die "create raid6 failed" ++ ++ # trigger dirty pages write back ++ echo 0 > /proc/sys/vm/dirty_background_ratio ++} ++ ++clean_up_test() ++{ ++ echo $old > /proc/sys/vm/dirty_background_ratio ++ ++ pkill -9 fio ++ kill -9 $pid ++ ++ sleep 1 ++ ++ if ps $pid | tail -1 | awk '{print $3}' | grep D; then ++ die "thread that is writing sysfs is stuck in D state, deadlock is triggered" ++ fi ++ mdadm -S $md0 ++} ++ ++trap 'clean_up_test' EXIT ++ ++set_up_test || die "set up test failed" ++ ++test_write_back & ++ ++test_write_action & ++pid="$!" ++ ++sleep $runtime ++ ++exit 0 +-- +2.40.1 + diff --git a/0148-tests-add-a-regression-test-that-raid456-can-t-assem.patch b/0148-tests-add-a-regression-test-that-raid456-can-t-assem.patch new file mode 100644 index 0000000..a10b3b2 --- /dev/null +++ b/0148-tests-add-a-regression-test-that-raid456-can-t-assem.patch @@ -0,0 +1,59 @@ +From 4db8a3d452daaf2def41ca45c36e36c5781627ac Mon Sep 17 00:00:00 2001 +From: Yu Kuai +Date: Mon, 29 May 2023 21:28:23 +0800 +Subject: [PATCH 148/165] tests: add a regression test that raid456 can't + assemble + +If recovery is interrupted and reshape is started, then this array can't +assemble anymore. The problem is supposed to be fixed by [1]. + +[1] https://lore.kernel.org/linux-raid/20230529031045.1760883-1-yukuai1@huaweicloud.com/ + +Signed-off-by: Yu Kuai +Signed-off-by: Jes Sorensen +--- + tests/25raid456-reshape-while-recovery | 32 ++++++++++++++++++++++++++ + 1 file changed, 32 insertions(+) + create mode 100644 tests/25raid456-reshape-while-recovery + +diff --git a/tests/25raid456-reshape-while-recovery b/tests/25raid456-reshape-while-recovery +new file mode 100644 +index 00000000..b9f871f2 +--- /dev/null ++++ b/tests/25raid456-reshape-while-recovery +@@ -0,0 +1,32 @@ ++devs="$dev0 $dev1 $dev2" ++ ++set_up_test() ++{ ++ mdadm -Cv -R -n 3 -l5 $md0 $devs --assume-clean --size=50M || die "create array failed" ++ mdadm -a $md0 $dev3 $dev4 || die "failed to bind new disk to array" ++ echo 1000 > /sys/block/md0/md/sync_speed_max ++} ++ ++clean_up_test() ++{ ++ mdadm -S $md0 ++} ++ ++trap 'clean_up_test' EXIT ++ ++set_up_test || die "set up test failed" ++ ++# set up replacement ++echo want_replacement > /sys/block/md0/md/rd0/state ++sleep 1 ++ ++# trigger reshape ++echo frozen > /sys/block/md0/md/sync_action ++mdadm --grow -l 6 $md0 ++sleep 1 ++ ++# reassemeble array ++mdadm -S $md0 || die "can't stop array" ++mdadm --assemble $md0 $devs $dev3 $dev4 || die "can't assemble array" ++ ++exit 0 +-- +2.40.1 + diff --git a/0149-tests-add-a-regression-test-that-raid456-can-t-assem.patch b/0149-tests-add-a-regression-test-that-raid456-can-t-assem.patch new file mode 100644 index 0000000..3d52868 --- /dev/null +++ b/0149-tests-add-a-regression-test-that-raid456-can-t-assem.patch @@ -0,0 +1,58 @@ +From 8d45abd0c317d3f4971b779b4f8d1e33b5490dac Mon Sep 17 00:00:00 2001 +From: Yu Kuai +Date: Mon, 29 May 2023 21:28:24 +0800 +Subject: [PATCH 149/165] tests: add a regression test that raid456 can't + assemble again + +This is a regression test for commit 0aecb06e2249 ("md/raid5: don't allow +replacement while reshape is in progress"). + +Signed-off-by: Yu Kuai +Signed-off-by: Jes Sorensen +--- + tests/25raid456-recovery-while-reshape | 33 ++++++++++++++++++++++++++ + 1 file changed, 33 insertions(+) + create mode 100644 tests/25raid456-recovery-while-reshape + +diff --git a/tests/25raid456-recovery-while-reshape b/tests/25raid456-recovery-while-reshape +new file mode 100644 +index 00000000..3f6251bf +--- /dev/null ++++ b/tests/25raid456-recovery-while-reshape +@@ -0,0 +1,33 @@ ++devs="$dev0 $dev1 $dev2" ++ ++set_up_test() ++{ ++ mdadm -Cv -R -n 3 -l5 $md0 $devs --assume-clean --size=50M || die "create array failed" ++ mdadm -a $md0 $dev3 $dev4 || die "failed to bind new disk to array" ++ echo 1000 > /sys/block/md0/md/sync_speed_max ++} ++ ++clean_up_test() ++{ ++ mdadm -S $md0 ++} ++ ++trap 'clean_up_test' EXIT ++ ++set_up_test || die "set up test failed" ++ ++# trigger reshape ++mdadm --grow -l 6 $md0 ++sleep 1 ++ ++# set up replacement ++echo frozen > /sys/block/md0/md/sync_action ++echo want_replacement > /sys/block/md0/md/rd0/state ++echo reshape > /sys/block/md0/md/sync_action ++sleep 1 ++ ++# reassemeble array ++mdadm -S $md0 || die "can't stop array" ++mdadm --assemble $md0 $devs $dev3 $dev4 || die "can't assemble array" ++ ++exit 0 +-- +2.40.1 + diff --git a/0150-tests-add-a-regression-test-that-reshape-can-corrupt.patch b/0150-tests-add-a-regression-test-that-reshape-can-corrupt.patch new file mode 100644 index 0000000..20f0272 --- /dev/null +++ b/0150-tests-add-a-regression-test-that-reshape-can-corrupt.patch @@ -0,0 +1,60 @@ +From 481433482942c2ee2990965af394eff059990901 Mon Sep 17 00:00:00 2001 +From: Yu Kuai +Date: Mon, 29 May 2023 21:28:25 +0800 +Subject: [PATCH 150/165] tests: add a regression test that reshape can corrupt + data + +This is a regression test for commit 1544e95c6dd8 ("md: fix data +corruption for raid456 when reshape restart while grow up"). + +Signed-off-by: Yu Kuai +Signed-off-by: Jes Sorensen +--- + tests/25raid456-reshape-corrupt-data | 35 ++++++++++++++++++++++++++++ + 1 file changed, 35 insertions(+) + create mode 100644 tests/25raid456-reshape-corrupt-data + +diff --git a/tests/25raid456-reshape-corrupt-data b/tests/25raid456-reshape-corrupt-data +new file mode 100644 +index 00000000..fdb875fb +--- /dev/null ++++ b/tests/25raid456-reshape-corrupt-data +@@ -0,0 +1,35 @@ ++devs="$dev0 $dev1 $dev2" ++ ++set_up_test() ++{ ++ mdadm -Cv -R -n 3 -l5 $md0 $devs --size=50M || die "create array failed" ++ mdadm -a $md0 $dev3 || die "failed to bind new disk to array" ++ mkfs.xfs -f $md0 || die "mkfs failed" ++ xfs_ncheck $md0 || die "check fs failed" ++} ++ ++clean_up_test() ++{ ++ mdadm -S $md0 ++} ++ ++trap 'clean_up_test' EXIT ++ ++set_up_test || die "set up test failed" ++ ++# trigger reshape ++echo 1000 > /sys/block/md0/md/sync_speed_max ++mdadm --grow -l 6 $md0 ++sleep 1 ++ ++# stop and start reshape ++echo frozen > /sys/block/md0/md/sync_action ++echo system > /sys/block/md0/md/sync_speed_max ++echo reshape > /sys/block/md0/md/sync_action ++ ++mdadm -W $md0 ++ ++# check if data is corrupted ++xfs_ncheck $md0 || die "data is corrupted after reshape" ++ ++exit 0 +-- +2.40.1 + diff --git a/0151-tests-add-a-regression-test-for-raid456-deadlock-aga.patch b/0151-tests-add-a-regression-test-for-raid456-deadlock-aga.patch new file mode 100644 index 0000000..ab916c5 --- /dev/null +++ b/0151-tests-add-a-regression-test-for-raid456-deadlock-aga.patch @@ -0,0 +1,59 @@ +From c6c7bce5a3064ac9d2956ae42ecab27f2e33dc2b Mon Sep 17 00:00:00 2001 +From: Yu Kuai +Date: Mon, 29 May 2023 21:28:26 +0800 +Subject: [PATCH 151/165] tests: add a regression test for raid456 deadlock + again + +This is a regression test for commit ("md/raid5: fix a deadlock in the +case that reshape is interrupted"). + +Signed-off-by: Yu Kuai +Signed-off-by: Jes Sorensen +--- + tests/25raid456-reshape-deadlock | 34 ++++++++++++++++++++++++++++++++ + 1 file changed, 34 insertions(+) + create mode 100644 tests/25raid456-reshape-deadlock + +diff --git a/tests/25raid456-reshape-deadlock b/tests/25raid456-reshape-deadlock +new file mode 100644 +index 00000000..bfa0cc56 +--- /dev/null ++++ b/tests/25raid456-reshape-deadlock +@@ -0,0 +1,34 @@ ++devs="$dev0 $dev1 $dev2" ++ ++set_up_test() ++{ ++ mdadm -Cv -R -n 3 -l5 $md0 $devs --size=50M || die "create array failed" ++ mdadm -a $md0 $dev3 || die "failed to bind new disk to array" ++ echo 1000 > /sys/block/md0/md/sync_speed_max ++} ++ ++clean_up_test() ++{ ++ echo idle > /sys/block/md0/md/sync_action ++ mdadm -S $md0 ++} ++ ++trap 'clean_up_test' EXIT ++ ++set_up_test || die "set up test failed" ++ ++# trigger reshape ++mdadm --grow -l 6 $md0 ++sleep 1 ++ ++# stop reshape ++echo frozen > /sys/block/md0/md/sync_action ++ ++# read accross reshape ++dd if=$md0 of=/dev/NULL bs=1m count=100 iflag=direct &> /dev/null & ++sleep 2 ++ ++# suspend array ++echo 1 > /sys/block/md0/md/suspend_lo ++ ++exit 0 +-- +2.40.1 + diff --git a/0152-tests-create-names_template.patch b/0152-tests-create-names_template.patch new file mode 100644 index 0000000..a3db688 --- /dev/null +++ b/0152-tests-create-names_template.patch @@ -0,0 +1,193 @@ +From d5fee8654e41dc4067dfdc3312542f1f43ebe884 Mon Sep 17 00:00:00 2001 +From: Mariusz Tkaczyk +Date: Thu, 1 Jun 2023 09:27:45 +0200 +Subject: [PATCH 152/165] tests: create names_template + +Create templates directory and names_template. Move code from +00createnames. This code will be reused for 00confnames in next patch. + +Signed-off-by: Mariusz Tkaczyk +Signed-off-by: Jes Sorensen +--- + tests/00createnames | 86 +++++++--------------------------- + tests/templates/names_template | 53 +++++++++++++++++++++ + 2 files changed, 70 insertions(+), 69 deletions(-) + create mode 100644 tests/templates/names_template + +diff --git a/tests/00createnames b/tests/00createnames +index 64b81b92..064eeef2 100644 +--- a/tests/00createnames ++++ b/tests/00createnames +@@ -1,93 +1,41 @@ + set -x -e ++. tests/templates/names_template + + # Test how and --name= are handled for create mode. +-# We need to check three properties, generated from those parameters: +-# - devnode name +-# - link in /dev/md/ (MD_DEVNAME property from --detail --export) +-# - name in metadata (MD_NAME property from --examine --export) +- +-function _verify() { +- local DEVNODE_NAME="$1" +- local WANTED_LINK="$2" +- local WANTED_NAME="$3" +- +- local RES="$(mdadm -D --export $DEVNODE_NAME | grep MD_DEVNAME)" +- if [[ "$?" != "0" ]]; then +- echo "Cannot get details for $DEVNODE_NAME - unexpected devnode." +- exit 1 +- fi +- +- if [[ "$WANTED_LINK" != "empty" ]]; then +- local EXPECTED="MD_DEVNAME=$WANTED_LINK" +- if [[ "$RES" != "$EXPECTED" ]]; then +- echo "$RES doesn't match $EXPECTED." +- exit 1 +- fi +- fi +- +- +- local RES="$(mdadm -E --export $dev0 | grep MD_NAME)" +- if [[ "$?" != "0" ]]; then +- echo "Cannot get metadata from $dev0." +- exit 1 +- fi +- +- local EXPECTED="MD_NAME=$(hostname):$WANTED_NAME" +- if [[ "$RES" != "$EXPECTED" ]]; then +- echo "$RES doesn't match $EXPECTED." +- exit 1 +- fi +-} +- +-function _create() { +- local DEVNAME=$1 +- local NAME=$2 +- +- if [[ -z "$NAME" ]]; then +- mdadm -CR "$DEVNAME" -l0 -n 1 $dev0 --force +- else +- mdadm -CR "$DEVNAME" --name="$NAME" -l0 -n 1 $dev0 --force +- fi +- +- if [[ "$?" != "0" ]]; then +- echo "Cannot create device." +- exit 1 +- fi +-} + + # The most trivial case. +-_create "/dev/md/name" +-_verify "/dev/md127" "name" "name" ++names_create "/dev/md/name" ++names_verify "/dev/md127" "name" "name" + mdadm -S "/dev/md127" + +-_create "name" +-_verify "/dev/md127" "name" "name" ++names_create "name" ++names_verify "/dev/md127" "name" "name" + mdadm -S "/dev/md127" + + # Use 'mdX' as name. +-_create "/dev/md/md0" +-_verify "/dev/md127" "md0" "md0" ++names_create "/dev/md/md0" ++names_verify "/dev/md127" "md0" "md0" + mdadm -S "/dev/md127" + +-_create "md0" +-_verify "/dev/md127" "md0" "md0" ++names_create "md0" ++names_verify "/dev/md127" "md0" "md0" + mdadm -S "/dev/md127" + + # is used to create MD_DEVNAME but, name is used to create MD_NAME. +-_create "/dev/md/devnode" "name" +-_verify "/dev/md127" "devnode" "name" ++names_create "/dev/md/devnode" "name" ++names_verify "/dev/md127" "devnode" "name" + mdadm -S "/dev/md127" + +-_create "devnode" "name" +-_verify "/dev/md127" "devnode" "name" ++names_create "devnode" "name" ++names_verify "/dev/md127" "devnode" "name" + mdadm -S "/dev/md127" + + # Devnode points to /dev/ directory. MD_DEVNAME doesn't exist. +-_create "/dev/md0" +-_verify "/dev/md0" "empty" "0" ++names_create "/dev/md0" ++names_verify "/dev/md0" "empty" "0" + mdadm -S "/dev/md0" + + # Devnode points to /dev/ directory and name is set. +-_create "/dev/md0" "name" +-_verify "/dev/md0" "empty" "name" ++names_create "/dev/md0" "name" ++names_verify "/dev/md0" "empty" "name" + mdadm -S "/dev/md0" +diff --git a/tests/templates/names_template b/tests/templates/names_template +new file mode 100644 +index 00000000..9f09be9e +--- /dev/null ++++ b/tests/templates/names_template +@@ -0,0 +1,53 @@ ++# NAME is optional. Testing with native 1.2 superblock. ++function names_create() { ++ local DEVNAME=$1 ++ local NAME=$2 ++ ++ if [[ -z "$NAME" ]]; then ++ mdadm -CR "$DEVNAME" -l0 -n 1 $dev0 --force ++ else ++ mdadm -CR "$DEVNAME" --name="$NAME" --metadata=1.2 -l0 -n 1 $dev0 --force ++ fi ++ ++ if [[ "$?" != "0" ]]; then ++ echo "Cannot create device." ++ exit 1 ++ fi ++} ++ ++# Three properties to check: ++# - devnode name ++# - link in /dev/md/ (MD_DEVNAME property from --detail --export) ++# - name in metadata (MD_NAME property from --detail --export)- that works only with 1.2 sb. ++function names_verify() { ++ local DEVNODE_NAME="$1" ++ local WANTED_LINK="$2" ++ local WANTED_NAME="$3" ++ ++ local RES="$(mdadm -D --export $DEVNODE_NAME | grep MD_DEVNAME)" ++ if [[ "$?" != "0" ]]; then ++ echo "Cannot get details for $DEVNODE_NAME - unexpected devnode." ++ exit 1 ++ fi ++ ++ if [[ "$WANTED_LINK" != "empty" ]]; then ++ local EXPECTED="MD_DEVNAME=$WANTED_LINK" ++ fi ++ ++ if [[ "$RES" != "$EXPECTED" ]]; then ++ echo "$RES doesn't match $EXPECTED." ++ exit 1 ++ fi ++ ++ local RES="$(mdadm -D --export $DEVNODE_NAME | grep MD_NAME)" ++ if [[ "$?" != "0" ]]; then ++ echo "Cannot get metadata from $dev0." ++ exit 1 ++ fi ++ ++ local EXPECTED="MD_NAME=$(hostname):$WANTED_NAME" ++ if [[ "$RES" != "$EXPECTED" ]]; then ++ echo "$RES doesn't match $EXPECTED." ++ exit 1 ++ fi ++} +-- +2.40.1 + diff --git a/0153-tests-create-00confnames.patch b/0153-tests-create-00confnames.patch new file mode 100644 index 0000000..1534d93 --- /dev/null +++ b/0153-tests-create-00confnames.patch @@ -0,0 +1,164 @@ +From 569a23425939e4bfc7b4d7f871d8510bb968f892 Mon Sep 17 00:00:00 2001 +From: Mariusz Tkaczyk +Date: Thu, 1 Jun 2023 09:27:46 +0200 +Subject: [PATCH 153/165] tests: create 00confnames + +The test is an attempt to document current implementation of devnode +and name handling for config entries. It is focused on incremental- +default way of array assembling on boot. +The expectations are aligned to current implementation for native +metadata because it is the most complicated scenario- both variables +can be set. + +Signed-off-by: Mariusz Tkaczyk +Signed-off-by: Jes Sorensen +--- + tests/00confnames | 107 +++++++++++++++++++++++++++++++++ + tests/templates/names_template | 20 ++++++ + 2 files changed, 127 insertions(+) + create mode 100644 tests/00confnames + +diff --git a/tests/00confnames b/tests/00confnames +new file mode 100644 +index 00000000..25a7127b +--- /dev/null ++++ b/tests/00confnames +@@ -0,0 +1,107 @@ ++set -x -e ++. tests/templates/names_template ++ ++# Test how and from config are handled during Incremental assemblation. ++# 1-6 only tests (no in config). ++# 6-10 and combinations are tested. ++# 11-13 corner cases. ++ ++names_create "/dev/md/name" ++local _UUID="$(mdadm -D --export /dev/md127 | grep MD_UUID | cut -d'=' -f2)" ++[[ "$_UUID" == "" ]] && echo "Cannot obtain UUID for $DEVNODE_NAME" && exit 1 ++ ++ ++# 1. definition consistent with metadata name. ++names_make_conf $_UUID "/dev/md/name" "empty" $config ++mdadm -S "/dev/md127" ++mdadm -I $dev0 --config=$config ++names_verify "/dev/md127" "name" "name" ++mdadm -S "/dev/md127" ++ ++# 2. Same as 1, but use short name form of . ++names_make_conf $_UUID "name" "empty" $config ++mdadm -I $dev0 --config=$config ++names_verify "/dev/md127" "name" "name" ++mdadm -S "/dev/md127" ++ ++# 3. Same as 1, but use different than metadata provides. ++names_make_conf $_UUID "/dev/md/other" "empty" $config ++mdadm -I $dev0 --config=$config ++names_verify "/dev/md127" "other" "name" ++mdadm -S "/dev/md127" ++ ++# 4. Same as 3, but use short name form of . ++names_make_conf $_UUID "other" "empty" $config ++mdadm -I $dev0 --config=$config ++names_verify "/dev/md127" "other" "name" ++mdadm -S "/dev/md127" ++ ++# 5. Force particular node creation by setting to /dev/mdX. Link is not created in this ++# case. ++names_make_conf $_UUID "/dev/md4" "empty" $config ++mdadm -I $dev0 --config=$config ++names_verify "/dev/md4" "empty" "name" ++mdadm -S "/dev/md4" ++ ++# 6. set to /dev/mdX, same as in metadata. ++# Metadata name and default node used - controversial. Current behavior documented. ++names_make_conf $_UUID "/dev/md22" "name" $config ++mdadm -I $dev0 --config=$config ++names_verify "/dev/md127" "name" "name" ++mdadm -S "/dev/md127" ++ ++# 7. set to /dev/mdX, different than in metadata. ++# Metadata name and default node used - controversial. Current behavior documented. ++names_make_conf $_UUID "/dev/md8" "other" $config ++mdadm -I $dev0 --config=$config ++names_verify "/dev/md127" "name" "name" ++mdadm -S "/dev/md127" ++ ++# 8. Both and different than in metadata. ++# Metadata name and default node used - controversial. Current behavior documented. ++names_make_conf $_UUID "devnode" "other_name" $config ++mdadm -I $dev0 --config=$config ++names_verify "/dev/md127" "name" "name" ++mdadm -S "/dev/md127" ++ ++# 9. set to metadata name, different than in metadata. ++# Metadata name and default node used - controversial. Current behavior documented. ++names_make_conf $_UUID "name" "other_name" $config ++mdadm -I $dev0 --config=$config ++names_verify "/dev/md127" "name" "name" ++mdadm -S "/dev/md127" ++ ++# 10. Bad set, no . ++# Metadata name and default node used - expected. ++names_make_conf $_UUID "/im/bad/devname" "empty" $config ++mdadm -I $dev0 --config=$config ++names_verify "/dev/md127" "name" "name" ++mdadm -S "/dev/md127" ++ ++# 11. with some special symbols and locales, no . ++# It needs to wait a while for timeout because udev cannot create a link - known issue. ++names_make_conf $_UUID "tźż-\.,<>st+-" "empty" $config ++mdadm -I $dev0 --config=$config ++names_verify "/dev/md127" "tźż-\.,<>st+-" "name" ++mdadm -S "/dev/md127" ++ ++# 12. No and set. ++# Metadata name and default node used - expected. ++names_make_conf $_UUID "empty" "empty" $config ++mdadm -I $dev0 --config=$config ++names_verify "/dev/md127" "name" "name" ++mdadm -S "/dev/md127" ++ ++# 13. No , set to /dev/mdX. ++# Entry should be ignored, it is not ignored but result is good anyway. ++names_make_conf $_UUID "empty" "/dev/md12" $config ++mdadm -I $dev0 --config=$config ++names_verify "/dev/md127" "name" "name" ++mdadm -S "/dev/md127" ++ ++# 13. No , with special symbols and locales. ++# Entry should be ignored, it is not ignored but result is good anyway. ++names_make_conf $_UUID "empty" "./\śćń#&" $config ++mdadm -I $dev0 --config=$config ++names_verify "/dev/md127" "name" "name" ++mdadm -S "/dev/md127" +diff --git a/tests/templates/names_template b/tests/templates/names_template +index 9f09be9e..8d2b5c81 100644 +--- a/tests/templates/names_template ++++ b/tests/templates/names_template +@@ -51,3 +51,23 @@ function names_verify() { + exit 1 + fi + } ++ ++# Generate ARRAYLINE for tested array. ++names_make_conf() { ++ local UUID="$1" ++ local WANTED_DEVNAME="$2" ++ local WANTED_NAME="$3" ++ local CONF="$4" ++ ++ local LINE="ARRAY metadata=1.2 UUID=$UUID" ++ ++ if [[ "$WANTED_DEVNAME" != "empty" ]]; then ++ LINE="$LINE $WANTED_DEVNAME" ++ fi ++ ++ if [[ "$WANTED_NAME" != "empty" ]]; then ++ LINE="$LINE name=$WANTED_NAME" ++ fi ++ ++ echo $LINE > $CONF ++} +-- +2.40.1 + diff --git a/0154-mdadm-set-ident.devname-if-applicable.patch b/0154-mdadm-set-ident.devname-if-applicable.patch new file mode 100644 index 0000000..14ca30e --- /dev/null +++ b/0154-mdadm-set-ident.devname-if-applicable.patch @@ -0,0 +1,399 @@ +From 330c07f8e4d26f4f2d068e11f09c9df8a3380087 Mon Sep 17 00:00:00 2001 +From: Mariusz Tkaczyk +Date: Thu, 1 Jun 2023 09:27:47 +0200 +Subject: [PATCH 154/165] mdadm: set ident.devname if applicable + +This patch tries to propagate the usage of struct mddev_ident for cmdline +where it is applicable. To avoid regression, this value is derived +from devlist->devname for applicable modes only. +As a result, the whole structure is passed to some functions. It produces +some changes for Build, Create and Assemble. +No functional changes intended. + +The goal of the change is to unify devname validation which is done in +next patches. + +Signed-off-by: Mariusz Tkaczyk +Signed-off-by: Jes Sorensen +--- + Build.c | 21 +++++++++------------ + Create.c | 35 ++++++++++++++++------------------ + mdadm.c | 57 +++++++++++++++++++++++++------------------------------- + mdadm.h | 13 +++++-------- + 4 files changed, 55 insertions(+), 71 deletions(-) + +diff --git a/Build.c b/Build.c +index 8d6f6f58..657ab315 100644 +--- a/Build.c ++++ b/Build.c +@@ -24,8 +24,8 @@ + + #include "mdadm.h" + +-int Build(char *mddev, struct mddev_dev *devlist, +- struct shape *s, struct context *c) ++int Build(struct mddev_ident *ident, struct mddev_dev *devlist, struct shape *s, ++ struct context *c) + { + /* Build a linear or raid0 arrays without superblocks + * We cannot really do any checks, we just do it. +@@ -75,13 +75,12 @@ int Build(char *mddev, struct mddev_dev *devlist, + + /* We need to create the device. It can have no name. */ + map_lock(&map); +- mdfd = create_mddev(mddev, NULL, c->autof, LOCAL, ++ mdfd = create_mddev(ident->devname, NULL, c->autof, LOCAL, + chosen_name, 0); + if (mdfd < 0) { + map_unlock(&map); + return 1; + } +- mddev = chosen_name; + + map_update(&map, fd2devnm(mdfd), "none", uuid, chosen_name); + map_unlock(&map); +@@ -93,7 +92,7 @@ int Build(char *mddev, struct mddev_dev *devlist, + array.nr_disks = s->raiddisks; + array.raid_disks = s->raiddisks; + array.md_minor = 0; +- if (fstat_is_blkdev(mdfd, mddev, &rdev)) ++ if (fstat_is_blkdev(mdfd, chosen_name, &rdev)) + array.md_minor = minor(rdev); + array.not_persistent = 1; + array.state = 0; /* not clean, but no errors */ +@@ -108,8 +107,7 @@ int Build(char *mddev, struct mddev_dev *devlist, + array.chunk_size = s->chunk*1024; + array.layout = s->layout; + if (md_set_array_info(mdfd, &array)) { +- pr_err("md_set_array_info() failed for %s: %s\n", +- mddev, strerror(errno)); ++ pr_err("md_set_array_info() failed for %s: %s\n", chosen_name, strerror(errno)); + goto abort; + } + +@@ -178,8 +176,8 @@ int Build(char *mddev, struct mddev_dev *devlist, + } + if (bitmap_fd >= 0) { + if (ioctl(mdfd, SET_BITMAP_FILE, bitmap_fd) < 0) { +- pr_err("Cannot set bitmap file for %s: %s\n", +- mddev, strerror(errno)); ++ pr_err("Cannot set bitmap file for %s: %s\n", chosen_name, ++ strerror(errno)); + goto abort; + } + } +@@ -193,9 +191,8 @@ int Build(char *mddev, struct mddev_dev *devlist, + } + + if (c->verbose >= 0) +- pr_err("array %s built and started.\n", +- mddev); +- wait_for(mddev, mdfd); ++ pr_err("array %s built and started.\n", chosen_name); ++ wait_for(chosen_name, mdfd); + close(mdfd); + return 0; + +diff --git a/Create.c b/Create.c +index ea6a4745..a280c7bc 100644 +--- a/Create.c ++++ b/Create.c +@@ -471,11 +471,8 @@ out: + return ret; + } + +-int Create(struct supertype *st, char *mddev, +- char *name, int *uuid, +- int subdevs, struct mddev_dev *devlist, +- struct shape *s, +- struct context *c) ++int Create(struct supertype *st, struct mddev_ident *ident, int subdevs, ++ struct mddev_dev *devlist, struct shape *s, struct context *c) + { + /* + * Create a new raid array. +@@ -497,6 +494,8 @@ int Create(struct supertype *st, char *mddev, + unsigned long long minsize = 0, maxsize = 0; + char *mindisc = NULL; + char *maxdisc = NULL; ++ char *name = ident->name; ++ int *uuid = ident->uuid_set == 1 ? ident->uuid : NULL; + int dnum; + struct mddev_dev *dv; + dev_t rdev; +@@ -1015,7 +1014,7 @@ int Create(struct supertype *st, char *mddev, + + /* We need to create the device */ + map_lock(&map); +- mdfd = create_mddev(mddev, name, c->autof, LOCAL, chosen_name, 1); ++ mdfd = create_mddev(ident->devname, ident->name, c->autof, LOCAL, chosen_name, 1); + if (mdfd < 0) { + map_unlock(&map); + return 1; +@@ -1032,7 +1031,6 @@ int Create(struct supertype *st, char *mddev, + udev_unblock(); + return 1; + } +- mddev = chosen_name; + + memset(&inf, 0, sizeof(inf)); + md_get_array_info(mdfd, &inf); +@@ -1050,7 +1048,7 @@ int Create(struct supertype *st, char *mddev, + * with, but it chooses to trust me instead. Sigh + */ + info.array.md_minor = 0; +- if (fstat_is_blkdev(mdfd, mddev, &rdev)) ++ if (fstat_is_blkdev(mdfd, chosen_name, &rdev)) + info.array.md_minor = minor(rdev); + info.array.not_persistent = 0; + +@@ -1102,8 +1100,8 @@ int Create(struct supertype *st, char *mddev, + info.array.layout = s->layout; + info.array.chunk_size = s->chunk*1024; + +- if (name == NULL || *name == 0) { +- /* base name on mddev */ ++ if (*name == 0) { ++ /* base name on devname */ + /* /dev/md0 -> 0 + * /dev/md_d0 -> d0 + * /dev/md_foo -> foo +@@ -1113,15 +1111,16 @@ int Create(struct supertype *st, char *mddev, + * /dev/mdhome -> home + */ + /* FIXME compare this with rules in create_mddev */ +- name = strrchr(mddev, '/'); ++ name = strrchr(chosen_name, '/'); ++ + if (name) { + name++; + if (strncmp(name, "md_", 3) == 0 && +- strlen(name) > 3 && (name-mddev) == 5 /* /dev/ */) ++ strlen(name) > 3 && (name - chosen_name) == 5 /* /dev/ */) + name += 3; + else if (strncmp(name, "md", 2) == 0 && + strlen(name) > 2 && isdigit(name[2]) && +- (name-mddev) == 5 /* /dev/ */) ++ (name - chosen_name) == 5 /* /dev/ */) + name += 2; + } + } +@@ -1215,8 +1214,7 @@ int Create(struct supertype *st, char *mddev, + } + rv = set_array_info(mdfd, st, &info); + if (rv) { +- pr_err("failed to set array info for %s: %s\n", +- mddev, strerror(errno)); ++ pr_err("failed to set array info for %s: %s\n", chosen_name, strerror(errno)); + goto abort_locked; + } + +@@ -1237,8 +1235,7 @@ int Create(struct supertype *st, char *mddev, + goto abort_locked; + } + if (ioctl(mdfd, SET_BITMAP_FILE, bitmap_fd) < 0) { +- pr_err("Cannot set bitmap file for %s: %s\n", +- mddev, strerror(errno)); ++ pr_err("Cannot set bitmap file for %s: %s\n", chosen_name, strerror(errno)); + goto abort_locked; + } + } +@@ -1254,7 +1251,7 @@ int Create(struct supertype *st, char *mddev, + * create links */ + sysfs_uevent(&info, "change"); + if (c->verbose >= 0) +- pr_err("container %s prepared.\n", mddev); ++ pr_err("container %s prepared.\n", chosen_name); + wait_for(chosen_name, mdfd); + } else if (c->runstop == 1 || subdevs >= s->raiddisks) { + if (st->ss->external) { +@@ -1312,7 +1309,7 @@ int Create(struct supertype *st, char *mddev, + ioctl(mdfd, RESTART_ARRAY_RW, NULL); + } + if (c->verbose >= 0) +- pr_info("array %s started.\n", mddev); ++ pr_info("array %s started.\n", chosen_name); + if (st->ss->external && st->container_devnm[0]) { + if (need_mdmon) + start_mdmon(st->container_devnm); +diff --git a/mdadm.c b/mdadm.c +index 22d1c53b..0a56ed26 100644 +--- a/mdadm.c ++++ b/mdadm.c +@@ -1290,37 +1290,39 @@ int main(int argc, char *argv[]) + pr_err("an md device must be given in this mode\n"); + exit(2); + } ++ ident.devname = devlist->devname; ++ + if ((int)ident.super_minor == -2 && c.autof) { + pr_err("--super-minor=dev is incompatible with --auto\n"); + exit(2); + } + if (mode == MANAGE || mode == GROW) { +- mdfd = open_mddev(devlist->devname, 1); ++ mdfd = open_mddev(ident.devname, 1); + if (mdfd < 0) + exit(1); + + ret = fstat(mdfd, &stb); + if (ret) { +- pr_err("fstat failed on %s.\n", devlist->devname); ++ pr_err("fstat failed on %s.\n", ident.devname); + exit(1); + } + } else { +- char *bname = basename(devlist->devname); ++ char *bname = basename(ident.devname); + + if (strlen(bname) > MD_NAME_MAX) { +- pr_err("Name %s is too long.\n", devlist->devname); ++ pr_err("Name %s is too long.\n", ident.devname); + exit(1); + } + +- ret = stat(devlist->devname, &stb); ++ ret = stat(ident.devname, &stb); + if (ident.super_minor == -2 && ret != 0) { + pr_err("--super-minor=dev given, and listed device %s doesn't exist.\n", +- devlist->devname); ++ ident.devname); + exit(1); + } + + if (!ret && !stat_is_md_dev(&stb)) { +- pr_err("device %s exists but is not an md array.\n", devlist->devname); ++ pr_err("device %s exists but is not an md array.\n", ident.devname); + exit(1); + } + } +@@ -1408,17 +1410,17 @@ int main(int argc, char *argv[]) + case MANAGE: + /* readonly, add/remove, readwrite, runstop */ + if (c.readonly > 0) +- rv = Manage_ro(devlist->devname, mdfd, c.readonly); ++ rv = Manage_ro(ident.devname, mdfd, c.readonly); + if (!rv && devs_found > 1) +- rv = Manage_subdevs(devlist->devname, mdfd, ++ rv = Manage_subdevs(ident.devname, mdfd, + devlist->next, c.verbose, + c.test, c.update, c.force); + if (!rv && c.readonly < 0) +- rv = Manage_ro(devlist->devname, mdfd, c.readonly); ++ rv = Manage_ro(ident.devname, mdfd, c.readonly); + if (!rv && c.runstop > 0) +- rv = Manage_run(devlist->devname, mdfd, &c); ++ rv = Manage_run(ident.devname, mdfd, &c); + if (!rv && c.runstop < 0) +- rv = Manage_stop(devlist->devname, mdfd, c.verbose, 0); ++ rv = Manage_stop(ident.devname, mdfd, c.verbose, 0); + break; + case ASSEMBLE: + if (!c.scan && c.runstop == -1) { +@@ -1428,22 +1430,19 @@ int main(int argc, char *argv[]) + ident.super_minor == UnSet && ident.name[0] == 0 && + !c.scan) { + /* Only a device has been given, so get details from config file */ +- struct mddev_ident *array_ident = conf_get_ident(devlist->devname); ++ struct mddev_ident *array_ident = conf_get_ident(ident.devname); + if (array_ident == NULL) { +- pr_err("%s not identified in config file.\n", +- devlist->devname); ++ pr_err("%s not identified in config file.\n", ident.devname); + rv |= 1; + if (mdfd >= 0) + close(mdfd); + } else { + if (array_ident->autof == 0) + array_ident->autof = c.autof; +- rv |= Assemble(ss, devlist->devname, array_ident, +- NULL, &c); ++ rv |= Assemble(ss, ident.devname, array_ident, NULL, &c); + } + } else if (!c.scan) +- rv = Assemble(ss, devlist->devname, &ident, +- devlist->next, &c); ++ rv = Assemble(ss, ident.devname, &ident, devlist->next, &c); + else if (devs_found > 0) { + if (c.update && devs_found > 1) { + pr_err("can only update a single array at a time\n"); +@@ -1501,7 +1500,7 @@ int main(int argc, char *argv[]) + break; + } + } +- rv = Build(devlist->devname, devlist->next, &s, &c); ++ rv = Build(&ident, devlist->next, &s, &c); + break; + case CREATE: + if (c.delay == 0) +@@ -1538,9 +1537,7 @@ int main(int argc, char *argv[]) + break; + } + +- rv = Create(ss, devlist->devname, +- ident.name, ident.uuid_set ? ident.uuid : NULL, +- devs_found - 1, devlist->next, &s, &c); ++ rv = Create(ss, &ident, devs_found - 1, devlist->next, &s, &c); + break; + case MISC: + if (devmode == 'E') { +@@ -1637,8 +1634,7 @@ int main(int argc, char *argv[]) + break; + } + for (dv = devlist->next; dv; dv = dv->next) { +- rv = Grow_Add_device(devlist->devname, mdfd, +- dv->devname); ++ rv = Grow_Add_device(ident.devname, mdfd, dv->devname); + if (rv) + break; + } +@@ -1651,18 +1647,15 @@ int main(int argc, char *argv[]) + } + if (c.delay == 0) + c.delay = DEFAULT_BITMAP_DELAY; +- rv = Grow_addbitmap(devlist->devname, mdfd, &c, &s); ++ rv = Grow_addbitmap(ident.devname, mdfd, &c, &s); + } else if (grow_continue) +- rv = Grow_continue_command(devlist->devname, +- mdfd, c.backup_file, +- c.verbose); ++ rv = Grow_continue_command(ident.devname, mdfd, c.backup_file, c.verbose); + else if (s.size > 0 || s.raiddisks || s.layout_str || + s.chunk != 0 || s.level != UnSet || + s.data_offset != INVALID_SECTORS) { +- rv = Grow_reshape(devlist->devname, mdfd, +- devlist->next, &c, &s); ++ rv = Grow_reshape(ident.devname, mdfd, devlist->next, &c, &s); + } else if (s.consistency_policy != CONSISTENCY_POLICY_UNKNOWN) { +- rv = Grow_consistency_policy(devlist->devname, mdfd, &c, &s); ++ rv = Grow_consistency_policy(ident.devname, mdfd, &c, &s); + } else if (array_size == 0) + pr_err("no changes to --grow\n"); + break; +diff --git a/mdadm.h b/mdadm.h +index f0ceeb78..5678eb11 100644 +--- a/mdadm.h ++++ b/mdadm.h +@@ -1531,14 +1531,11 @@ extern int Assemble(struct supertype *st, char *mddev, + struct mddev_dev *devlist, + struct context *c); + +-extern int Build(char *mddev, struct mddev_dev *devlist, +- struct shape *s, struct context *c); +- +-extern int Create(struct supertype *st, char *mddev, +- char *name, int *uuid, +- int subdevs, struct mddev_dev *devlist, +- struct shape *s, +- struct context *c); ++extern int Build(struct mddev_ident *ident, struct mddev_dev *devlist, struct shape *s, ++ struct context *c); ++ ++extern int Create(struct supertype *st, struct mddev_ident *ident, int subdevs, ++ struct mddev_dev *devlist, struct shape *s, struct context *c); + + extern int Detail(char *dev, struct context *c); + extern int Detail_Platform(struct superswitch *ss, int scan, int verbose, int export, char *controller_path); +-- +2.40.1 + diff --git a/0155-mdadm-refactor-ident-name-handling.patch b/0155-mdadm-refactor-ident-name-handling.patch new file mode 100644 index 0000000..423b876 --- /dev/null +++ b/0155-mdadm-refactor-ident-name-handling.patch @@ -0,0 +1,253 @@ +From 67417d9222c505103357191bb0e0ae300892e8a9 Mon Sep 17 00:00:00 2001 +From: Mariusz Tkaczyk +Date: Thu, 1 Jun 2023 09:27:48 +0200 +Subject: [PATCH 155/165] mdadm: refactor ident->name handling + +Create dedicated setter for name in mddev_ident and propagate it. +Following changes are made: +- move duplicated code from config.c and mdadm.c into new function. +- Add error enum in mdadm.h. +- Use MD_NAME_MAX instead of hardcoded value in mddev_ident. +- Use secure functions. +- Add more detailed verification of the name. +- make error messages reusable for cmdline and config: + - for cmdline, these are errors so use pr_err(). + - for config, these are just warnings, so use pr_info(). + +Signed-off-by: Mariusz Tkaczyk +Signed-off-by: Jes Sorensen +--- + config.c | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++------ + lib.c | 18 +++++++++++++ + mdadm.c | 12 +++------ + mdadm.h | 20 ++++++++++----- + 4 files changed, 104 insertions(+), 23 deletions(-) + +diff --git a/config.c b/config.c +index 450880e3..a9a0b4f7 100644 +--- a/config.c ++++ b/config.c +@@ -131,6 +131,34 @@ bool is_devname_ignore(char *devname) + return false; + } + ++/** ++ * ident_log() - generate and write message to the user. ++ * @param_name: name of the property. ++ * @value: value of the property. ++ * @reason: meaningful description. ++ * @cmdline: context dependent actions, see below. ++ * ++ * The function is made to provide similar error handling for both config and cmdline. The behavior ++ * is configurable via @cmdline. Message has following format: ++ * "Value "@value" cannot be set for @param_name. Reason: @reason." ++ * ++ * If cmdline is on: ++ * - message is written to stderr. ++ * otherwise: ++ * - message is written to stdout. ++ * - "Value ignored" is added at the end of the message. ++ */ ++static void ident_log(const char *param_name, const char *value, const char *reason, ++ const bool cmdline) ++{ ++ if (cmdline == true) ++ pr_err("Value \"%s\" cannot be set as %s. Reason: %s.\n", value, param_name, ++ reason); ++ else ++ pr_info("Value \"%s\" cannot be set as %s. Reason: %s. Value ignored.\n", value, ++ param_name, reason); ++} ++ + /** + * ident_init() - Set defaults. + * @ident: ident pointer, not NULL. +@@ -159,6 +187,46 @@ inline void ident_init(struct mddev_ident *ident) + ident->uuid_set = 0; + } + ++/** ++ * _ident_set_name()- set name in &mddev_ident. ++ * @ident: pointer to &mddev_ident. ++ * @name: name to be set. ++ * @cmdline: context dependent actions. ++ * ++ * If criteria passed, set name in @ident. ++ * ++ * Return: %MDADM_STATUS_SUCCESS or %MDADM_STATUS_ERROR. ++ */ ++static mdadm_status_t _ident_set_name(struct mddev_ident *ident, const char *name, ++ const bool cmdline) ++{ ++ assert(name); ++ assert(ident); ++ ++ const char *prop_name = "name"; ++ ++ if (ident->name[0]) { ++ ident_log(prop_name, name, "Already defined", cmdline); ++ return MDADM_STATUS_ERROR; ++ } ++ ++ if (is_string_lq(name, MD_NAME_MAX + 1) == false) { ++ ident_log(prop_name, name, "Too long or empty", cmdline); ++ return MDADM_STATUS_ERROR; ++ } ++ ++ snprintf(ident->name, MD_NAME_MAX + 1, "%s", name); ++ return MDADM_STATUS_SUCCESS; ++} ++ ++/** ++ * ident_set_name()- exported, for cmdline. ++ */ ++mdadm_status_t ident_set_name(struct mddev_ident *ident, const char *name) ++{ ++ return _ident_set_name(ident, name, true); ++} ++ + struct conf_dev { + struct conf_dev *next; + char *name; +@@ -444,14 +512,7 @@ void arrayline(char *line) + mis.super_minor = minor; + } + } else if (strncasecmp(w, "name=", 5) == 0) { +- if (mis.name[0]) +- pr_err("only specify name once, %s ignored.\n", +- w); +- else if (strlen(w + 5) > 32) +- pr_err("name too long, ignoring %s\n", w); +- else +- strcpy(mis.name, w + 5); +- ++ _ident_set_name(&mis, w + 5, false); + } else if (strncasecmp(w, "bitmap=", 7) == 0) { + if (mis.bitmap_file) + pr_err("only specify bitmap file once. %s ignored\n", +diff --git a/lib.c b/lib.c +index 8a4b48e0..03198e2d 100644 +--- a/lib.c ++++ b/lib.c +@@ -27,6 +27,24 @@ + #include + #include + ++/** ++ * is_string_lq() - Check if string length with NULL byte is lower or equal to requested. ++ * @str: string to check. ++ * @max_len: max length. ++ * ++ * @str length must be bigger than 0 and be lower or equal @max_len, including termination byte. ++ */ ++bool is_string_lq(const char * const str, size_t max_len) ++{ ++ assert(str); ++ ++ size_t _len = strnlen(str, max_len); ++ ++ if (_len > 0 && _len < max_len) ++ return true; ++ return false; ++} ++ + bool is_dev_alive(char *path) + { + if (!path) +diff --git a/mdadm.c b/mdadm.c +index 0a56ed26..8bc9304e 100644 +--- a/mdadm.c ++++ b/mdadm.c +@@ -690,20 +690,14 @@ int main(int argc, char *argv[]) + case O(CREATE,'N'): + case O(ASSEMBLE,'N'): + case O(MISC,'N'): +- if (ident.name[0]) { +- pr_err("name cannot be set twice. Second value %s.\n", optarg); +- exit(2); +- } + if (mode == MISC && !c.subarray) { + pr_err("-N/--name only valid with --update-subarray in misc mode\n"); + exit(2); + } +- if (strlen(optarg) > 32) { +- pr_err("name '%s' is too long, 32 chars max.\n", +- optarg); ++ ++ if (ident_set_name(&ident, optarg) != MDADM_STATUS_SUCCESS) + exit(2); +- } +- strcpy(ident.name, optarg); ++ + continue; + + case O(ASSEMBLE,'m'): /* super-minor for array */ +diff --git a/mdadm.h b/mdadm.h +index 5678eb11..bbf386d6 100644 +--- a/mdadm.h ++++ b/mdadm.h +@@ -294,6 +294,11 @@ static inline void __put_unaligned32(__u32 val, void *p) + #define KIB_TO_BYTES(x) ((x) << 10) + #define SEC_TO_BYTES(x) ((x) << 9) + ++/** ++ * This is true for native and DDF, IMSM allows 16. ++ */ ++#define MD_NAME_MAX 32 ++ + extern const char Name[]; + + struct md_bb_entry { +@@ -425,6 +430,12 @@ struct spare_criteria { + unsigned int sector_size; + }; + ++typedef enum mdadm_status { ++ MDADM_STATUS_SUCCESS = 0, ++ MDADM_STATUS_ERROR, ++ MDADM_STATUS_UNDEF, ++} mdadm_status_t; ++ + enum mode { + ASSEMBLE=1, + BUILD, +@@ -593,7 +604,7 @@ struct mddev_ident { + + int uuid_set; + int uuid[4]; +- char name[33]; ++ char name[MD_NAME_MAX + 1]; + + int super_minor; + +@@ -1609,6 +1620,7 @@ extern int check_partitions(int fd, char *dname, + extern int fstat_is_blkdev(int fd, char *devname, dev_t *rdev); + extern int stat_is_blkdev(char *devname, dev_t *rdev); + ++extern bool is_string_lq(const char * const str, size_t max_len); + extern bool is_dev_alive(char *path); + extern int get_mdp_major(void); + extern int get_maj_min(char *dev, int *major, int *minor); +@@ -1626,6 +1638,7 @@ extern void manage_fork_fds(int close_all); + extern int continue_via_systemd(char *devnm, char *service_name, char *prefix); + + extern void ident_init(struct mddev_ident *ident); ++extern mdadm_status_t ident_set_name(struct mddev_ident *ident, const char *name); + + extern int parse_auto(char *str, char *msg, int config); + extern struct mddev_ident *conf_get_ident(char *dev); +@@ -2002,11 +2015,6 @@ enum r0layout { + /* And another special number needed for --data_offset=variable */ + #define VARIABLE_OFFSET 3 + +-/** +- * This is true for native and DDF, IMSM allows 16. +- */ +-#define MD_NAME_MAX 32 +- + /** + * is_container() - check if @level is &LEVEL_CONTAINER + * @level: level value +-- +2.40.1 + diff --git a/0156-mdadm-define-ident_set_devname.patch b/0156-mdadm-define-ident_set_devname.patch new file mode 100644 index 0000000..8c7441b --- /dev/null +++ b/0156-mdadm-define-ident_set_devname.patch @@ -0,0 +1,249 @@ +From ae5f13a971bc309e0e25087421119b86daf2e510 Mon Sep 17 00:00:00 2001 +From: Mariusz Tkaczyk +Date: Thu, 1 Jun 2023 09:27:49 +0200 +Subject: [PATCH 156/165] mdadm: define ident_set_devname() + +Use dedicated set method for ident->devname. Now, devname validation +is done early for modes where device is created (Build, Create and +Assemble). The rules, used for devname validation are derived from +config file. + +It could cause regression with execeptional cases where existing device +has name which doesn't match criteria for Manage and Grow modes. It is +low risk and those modes are not omitted from early devname validation. +Use can used main numbered devnode to avoid this problem. +Messages exposed to user are changed so it might cause a regression +in negative scenarios. Error codes are not changed. + +Signed-off-by: Mariusz Tkaczyk +Signed-off-by: Jes Sorensen +--- + config.c | 102 +++++++++++++++++++++++++-------- + mdadm.c | 10 +--- + mdadm.h | 3 +- + tests/00createnames | 3 + + tests/templates/names_template | 7 +++ + 5 files changed, 92 insertions(+), 33 deletions(-) + +diff --git a/config.c b/config.c +index a9a0b4f7..a0424845 100644 +--- a/config.c ++++ b/config.c +@@ -122,7 +122,7 @@ int match_keyword(char *word) + /** + * is_devname_ignore() - check if &devname is a special "" keyword. + */ +-bool is_devname_ignore(char *devname) ++bool is_devname_ignore(const char *devname) + { + static const char keyword[] = ""; + +@@ -187,6 +187,74 @@ inline void ident_init(struct mddev_ident *ident) + ident->uuid_set = 0; + } + ++/** ++ * _ident_set_devname()- verify devname and set it in &mddev_ident. ++ * @ident: pointer to &mddev_ident. ++ * @devname: devname to be set. ++ * @cmdline: context dependent actions. If set, ignore keyword is not allowed. ++ * ++ * @devname can have following forms: ++ * '' keyword (if allowed) ++ * /dev/md{number} ++ * /dev/md_d{number} (legacy) ++ * /dev/md_{name} ++ * /dev/md/{name} ++ * {name} - anything that doesn't start from '/' or '<'. ++ * ++ * {name} must follow name's criteria. ++ * If criteria passed, duplicate memory and set devname in @ident. ++ * ++ * Return: %MDADM_STATUS_SUCCESS or %MDADM_STATUS_ERROR. ++ */ ++mdadm_status_t _ident_set_devname(struct mddev_ident *ident, const char *devname, ++ const bool cmdline) ++{ ++ assert(ident); ++ assert(devname); ++ ++ static const char named_dev_pref[] = DEV_NUM_PREF "_"; ++ static const int named_dev_pref_size = sizeof(named_dev_pref) - 1; ++ const char *prop_name = "devname"; ++ const char *name; ++ ++ if (ident->devname) { ++ ident_log(prop_name, devname, "Already defined", cmdline); ++ return MDADM_STATUS_ERROR; ++ } ++ ++ if (is_devname_ignore(devname) == true) { ++ if (!cmdline) ++ goto pass; ++ ++ ident_log(prop_name, devname, "Special keyword is invalid in this context", ++ cmdline); ++ return MDADM_STATUS_ERROR; ++ } ++ ++ if (is_devname_md_numbered(devname) == true || is_devname_md_d_numbered(devname) == true) ++ goto pass; ++ ++ if (strncmp(devname, DEV_MD_DIR, DEV_MD_DIR_LEN) == 0) ++ name = devname + DEV_MD_DIR_LEN; ++ else if (strncmp(devname, named_dev_pref, named_dev_pref_size) == 0) ++ name = devname + named_dev_pref_size; ++ else ++ name = devname; ++ ++ if (*name == '/' || *name == '<') { ++ ident_log(prop_name, devname, "Cannot be started from \'/\' or \'<\'", cmdline); ++ return MDADM_STATUS_ERROR; ++ } ++ ++ if (is_string_lq(name, MD_NAME_MAX + 1) == false) { ++ ident_log(prop_name, devname, "Invalid length", cmdline); ++ return MDADM_STATUS_ERROR; ++ } ++pass: ++ ident->devname = xstrdup(devname); ++ return MDADM_STATUS_SUCCESS; ++} ++ + /** + * _ident_set_name()- set name in &mddev_ident. + * @ident: pointer to &mddev_ident. +@@ -219,6 +287,14 @@ static mdadm_status_t _ident_set_name(struct mddev_ident *ident, const char *nam + return MDADM_STATUS_SUCCESS; + } + ++/** ++ * ident_set_devname()- exported, for cmdline. ++ */ ++mdadm_status_t ident_set_devname(struct mddev_ident *ident, const char *name) ++{ ++ return _ident_set_devname(ident, name, true); ++} ++ + /** + * ident_set_name()- exported, for cmdline. + */ +@@ -464,29 +540,7 @@ void arrayline(char *line) + + for (w = dl_next(line); w != line; w = dl_next(w)) { + if (w[0] == '/' || strchr(w, '=') == NULL) { +- /* This names the device, or is ''. +- * The rules match those in create_mddev. +- * 'w' must be: +- * /dev/md/{anything} +- * /dev/mdNN +- * /dev/md_dNN +- * +- * or anything that doesn't start '/' or '<' +- */ +- if (is_devname_ignore(w) == true || +- strncmp(w, DEV_MD_DIR, DEV_MD_DIR_LEN) == 0 || +- (w[0] != '/' && w[0] != '<') || +- is_devname_md_numbered(w) == true || +- is_devname_md_d_numbered(w) == true) { +- /* This is acceptable */; +- if (mis.devname) +- pr_err("only give one device per ARRAY line: %s and %s\n", +- mis.devname, w); +- else +- mis.devname = w; +- }else { +- pr_err("%s is an invalid name for an md device - ignored.\n", w); +- } ++ _ident_set_devname(&mis, w, false); + } else if (strncasecmp(w, "uuid=", 5) == 0) { + if (mis.uuid_set) + pr_err("only specify uuid once, %s ignored.\n", +diff --git a/mdadm.c b/mdadm.c +index 8bc9304e..62f981df 100644 +--- a/mdadm.c ++++ b/mdadm.c +@@ -1284,7 +1284,8 @@ int main(int argc, char *argv[]) + pr_err("an md device must be given in this mode\n"); + exit(2); + } +- ident.devname = devlist->devname; ++ if (ident_set_devname(&ident, devlist->devname) != MDADM_STATUS_SUCCESS) ++ exit(1); + + if ((int)ident.super_minor == -2 && c.autof) { + pr_err("--super-minor=dev is incompatible with --auto\n"); +@@ -1301,13 +1302,6 @@ int main(int argc, char *argv[]) + exit(1); + } + } else { +- char *bname = basename(ident.devname); +- +- if (strlen(bname) > MD_NAME_MAX) { +- pr_err("Name %s is too long.\n", ident.devname); +- exit(1); +- } +- + ret = stat(ident.devname, &stb); + if (ident.super_minor == -2 && ret != 0) { + pr_err("--super-minor=dev given, and listed device %s doesn't exist.\n", +diff --git a/mdadm.h b/mdadm.h +index bbf386d6..49422e24 100644 +--- a/mdadm.h ++++ b/mdadm.h +@@ -1638,6 +1638,7 @@ extern void manage_fork_fds(int close_all); + extern int continue_via_systemd(char *devnm, char *service_name, char *prefix); + + extern void ident_init(struct mddev_ident *ident); ++extern mdadm_status_t ident_set_devname(struct mddev_ident *ident, const char *devname); + extern mdadm_status_t ident_set_name(struct mddev_ident *ident, const char *name); + + extern int parse_auto(char *str, char *msg, int config); +@@ -1660,7 +1661,7 @@ extern void print_escape(char *str); + extern int use_udev(void); + extern unsigned long GCD(unsigned long a, unsigned long b); + extern int conf_name_is_free(char *name); +-extern bool is_devname_ignore(char *devname); ++extern bool is_devname_ignore(const char *devname); + extern bool is_devname_md_numbered(const char *devname); + extern bool is_devname_md_d_numbered(const char *devname); + extern int conf_verify_devnames(struct mddev_ident *array_list); +diff --git a/tests/00createnames b/tests/00createnames +index 064eeef2..a95e7d2b 100644 +--- a/tests/00createnames ++++ b/tests/00createnames +@@ -39,3 +39,6 @@ mdadm -S "/dev/md0" + names_create "/dev/md0" "name" + names_verify "/dev/md0" "empty" "name" + mdadm -S "/dev/md0" ++ ++# Devnode is a special ignore keyword. Should be rejected. ++names_create "" "name", "true" +diff --git a/tests/templates/names_template b/tests/templates/names_template +index 8d2b5c81..6181bfaa 100644 +--- a/tests/templates/names_template ++++ b/tests/templates/names_template +@@ -2,6 +2,7 @@ + function names_create() { + local DEVNAME=$1 + local NAME=$2 ++ local NEG_TEST=$3 + + if [[ -z "$NAME" ]]; then + mdadm -CR "$DEVNAME" -l0 -n 1 $dev0 --force +@@ -9,6 +10,12 @@ function names_create() { + mdadm -CR "$DEVNAME" --name="$NAME" --metadata=1.2 -l0 -n 1 $dev0 --force + fi + ++ if [[ "$NEG_TEST" == "true" ]]; then ++ [[ "$?" == "0" ]] && return 0 ++ echo "Negative verification failed" ++ exit 1 ++ fi ++ + if [[ "$?" != "0" ]]; then + echo "Cannot create device." + exit 1 +-- +2.40.1 + diff --git a/0157-mdadm-Follow-POSIX-Portable-Character-Set.patch b/0157-mdadm-Follow-POSIX-Portable-Character-Set.patch new file mode 100644 index 0000000..50d2bbb --- /dev/null +++ b/0157-mdadm-Follow-POSIX-Portable-Character-Set.patch @@ -0,0 +1,471 @@ +From e2eb503bd797908f515b58428b274f1ba6a05349 Mon Sep 17 00:00:00 2001 +From: Mariusz Tkaczyk +Date: Thu, 1 Jun 2023 09:27:50 +0200 +Subject: [PATCH 157/165] mdadm: Follow POSIX Portable Character Set + +When the user creates a device with a name that contains whitespace, +mdadm timeouts and throws an error. This issue is caused by udev, which +truncates /dev/md link until the first whitespace. + +This patch introduces prohibition of characters other than A-Za-z0-9.-_ +in the device name. Also, it prohibits using leading "-" in device name, +so name won't be confused with cli parameter. +Set of allowed characters is taken from POSIX 3.280 Portable Character +Set. Also, device name length now is limited to NAME_MAX. + +In some places, there are other requirements for string length (e.g. size +up to MD_NAME_MAX for device name). This routine is made to follow POSIX +and other, more strict limitations should be checked separately. +We are aware of the risk of regression in exceptional cases (as +escape_devname function is removed) that should be fixed by updating +the array name. + +The POSIX validation is added for: +- 'name' parameter in every mode. +- first devlist entry, for Build, Create, Assemble, Manage, Grow. +- config entries, both devname and "name=". + +Additionally, some manual cleanups are made. + +Signed-off-by: Mariusz Tkaczyk +Signed-off-by: Jes Sorensen +--- + Detail.c | 17 ++++-------- + config.c | 13 ++++++--- + lib.c | 58 ++++++++++++++++++++++++++++----------- + mdadm.8.in | 70 ++++++++++++++++++++--------------------------- + mdadm.conf.5.in | 4 --- + mdadm.h | 2 +- + super-intel.c | 47 ++++++++++++++++--------------- + tests/00confnames | 4 +-- + 8 files changed, 113 insertions(+), 102 deletions(-) + +diff --git a/Detail.c b/Detail.c +index 206d88e3..57ac336f 100644 +--- a/Detail.c ++++ b/Detail.c +@@ -254,11 +254,9 @@ int Detail(char *dev, struct context *c) + fname_from_uuid(st, info, nbuf, ':'); + printf("MD_UUID=%s\n", nbuf + 5); + mp = map_by_uuid(&map, info->uuid); +- if (mp && mp->path && strncmp(mp->path, DEV_MD_DIR, DEV_MD_DIR_LEN) == 0) { +- printf("MD_DEVNAME="); +- print_escape(mp->path + DEV_MD_DIR_LEN); +- putchar('\n'); +- } ++ ++ if (mp && mp->path && strncmp(mp->path, DEV_MD_DIR, DEV_MD_DIR_LEN) == 0) ++ printf("MD_DEVNAME=%s\n", mp->path + DEV_MD_DIR_LEN); + + if (st->ss->export_detail_super) + st->ss->export_detail_super(st); +@@ -271,12 +269,9 @@ int Detail(char *dev, struct context *c) + __fname_from_uuid(mp->uuid, 0, nbuf, ':'); + printf("MD_UUID=%s\n", nbuf+5); + } +- if (mp && mp->path && +- strncmp(mp->path, DEV_MD_DIR, DEV_MD_DIR_LEN) == 0) { +- printf("MD_DEVNAME="); +- print_escape(mp->path + DEV_MD_DIR_LEN); +- putchar('\n'); +- } ++ if (mp && mp->path && strncmp(mp->path, DEV_MD_DIR, DEV_MD_DIR_LEN) == 0) ++ printf("MD_DEVNAME=%s\n", mp->path + DEV_MD_DIR_LEN); ++ + map_free(map); + } + if (!c->no_devices && sra) { +diff --git a/config.c b/config.c +index a0424845..5f12a1f8 100644 +--- a/config.c ++++ b/config.c +@@ -199,9 +199,9 @@ inline void ident_init(struct mddev_ident *ident) + * /dev/md_d{number} (legacy) + * /dev/md_{name} + * /dev/md/{name} +- * {name} - anything that doesn't start from '/' or '<'. ++ * {name} + * +- * {name} must follow name's criteria. ++ * {name} must follow name's criteria and be POSIX compatible. + * If criteria passed, duplicate memory and set devname in @ident. + * + * Return: %MDADM_STATUS_SUCCESS or %MDADM_STATUS_ERROR. +@@ -241,8 +241,8 @@ mdadm_status_t _ident_set_devname(struct mddev_ident *ident, const char *devname + else + name = devname; + +- if (*name == '/' || *name == '<') { +- ident_log(prop_name, devname, "Cannot be started from \'/\' or \'<\'", cmdline); ++ if (is_name_posix_compatible(name) == false) { ++ ident_log(prop_name, name, "Not POSIX compatible", cmdline); + return MDADM_STATUS_ERROR; + } + +@@ -283,6 +283,11 @@ static mdadm_status_t _ident_set_name(struct mddev_ident *ident, const char *nam + return MDADM_STATUS_ERROR; + } + ++ if (is_name_posix_compatible(name) == false) { ++ ident_log(prop_name, name, "Not POSIX compatible", cmdline); ++ return MDADM_STATUS_ERROR; ++ } ++ + snprintf(ident->name, MD_NAME_MAX + 1, "%s", name); + return MDADM_STATUS_SUCCESS; + } +diff --git a/lib.c b/lib.c +index 03198e2d..7ab59988 100644 +--- a/lib.c ++++ b/lib.c +@@ -483,24 +483,50 @@ void print_quoted(char *str) + putchar(q); + } + +-void print_escape(char *str) ++/** ++ * is_alphanum() - Check if sign is letter or digit. ++ * @c: char to analyze. ++ * ++ * Similar to isalnum() but additional locales are excluded. ++ * ++ * Return: %true on success, %false otherwise. ++ */ ++bool is_alphanum(const char c) + { +- /* print str, but change space and tab to '_' +- * as is suitable for device names +- */ +- for (; *str; str++) { +- switch (*str) { +- case ' ': +- case '\t': +- putchar('_'); +- break; +- case '/': +- putchar('-'); +- break; +- default: +- putchar(*str); +- } ++ if (isupper(c) || islower(c) || isdigit(c) != 0) ++ return true; ++ return false; ++} ++ ++/** ++ * is_name_posix_compatible() - Check if name is POSIX compatible. ++ * @name: name to check. ++ * ++ * POSIX portable file name character set contains ASCII letters, ++ * digits, '_', '.', and '-'. Also forbid leading '-'. ++ * The length of the name cannot exceed NAME_MAX - 1 (ensure NULL ending). ++ * ++ * Return: %true on success, %false otherwise. ++ */ ++bool is_name_posix_compatible(const char * const name) ++{ ++ assert(name); ++ ++ char allowed_symbols[] = "-_."; ++ const char *n = name; ++ ++ if (!is_string_lq(name, NAME_MAX)) ++ return false; ++ ++ if (*n == '-') ++ return false; ++ ++ while (*n != '\0') { ++ if (!is_alphanum(*n) && !strchr(allowed_symbols, *n)) ++ return false; ++ n++; + } ++ return true; + } + + int check_env(char *name) +diff --git a/mdadm.8.in b/mdadm.8.in +index b7159509..3142436f 100644 +--- a/mdadm.8.in ++++ b/mdadm.8.in +@@ -364,7 +364,7 @@ Use the Intel(R) Matrix Storage Manager metadata format. This creates a + which is managed in a similar manner to DDF, and is supported by an + option-rom on some platforms: + .IP +-.B https://www.intel.com/content/www/us/en/support/products/122484/memory-and-storage/ssd-software/intel-virtual-raid-on-cpu-intel-vroc.html ++.B https://www.intel.com/content/www/us/en/support/products/122484 + .PP + .RE + +@@ -932,17 +932,14 @@ option will be ignored. + .BR \-N ", " \-\-name= + Set a + .B name +-for the array. This is currently only effective when creating an +-array with a version-1 superblock, or an array in a DDF container. +-The name is a simple textual string that can be used to identify array +-components when assembling. If name is needed but not specified, it +-is taken from the basename of the device that is being created. +-e.g. when creating +-.I /dev/md/home +-the +-.B name +-will default to +-.IR home . ++for the array. It must be ++.BR "POSIX PORTABLE NAME" ++compatible and cannot be longer than 32 chars. This is effective when creating an array ++with a v1 metadata, or an external array. ++ ++If name is needed but not specified, it is taken from the basename of the device ++that is being created. See ++.BR "DEVICE NAMES" + + .TP + .BR \-R ", " \-\-run +@@ -1132,8 +1129,10 @@ is much safer. + + .TP + .BR \-N ", " \-\-name= +-Specify the name of the array to assemble. This must be the name +-that was specified when creating the array. It must either match ++Specify the name of the array to assemble. It must be ++.BR "POSIX PORTABLE NAME" ++compatible and cannot be longer than 32 chars. This must be the name ++that was specified when creating the array. It must either match + the name stored in the superblock exactly, or it must match + with the current + .I homehost +@@ -2179,14 +2178,17 @@ Usage: + .I md-device + .BI \-\-chunk= X + .BI \-\-level= Y +-.br + .BI \-\-raid\-devices= Z + .I devices + + .PP +-This usage will initialise a new md array, associate some devices with ++This usage will initialize a new md array, associate some devices with + it, and activate the array. + ++.I md-device ++is a new device. This could be standard name or chosen name. For details see: ++.BR "DEVICE NAMES" ++ + The named device will normally not exist when + .I "mdadm \-\-create" + is run, but will be created by +@@ -2227,24 +2229,6 @@ array. This feature can be overridden with the + .B \-\-force + option. + +-When creating an array with version-1 metadata a name for the array is +-required. +-If this is not given with the +-.B \-\-name +-option, +-.I mdadm +-will choose a name based on the last component of the name of the +-device being created. So if +-.B /dev/md3 +-is being created, then the name +-.B 3 +-will be chosen. +-If +-.B /dev/md/home +-is being created, then the name +-.B home +-will be used. +- + When creating a partition based array, using + .I mdadm + with version-1.x metadata, the partition type should be set to +@@ -2429,12 +2413,10 @@ and + + The + .B name +-option updates the subarray name in the metadata, it may not affect the +-device node name or the device node symlink until the subarray is +-re\-assembled. If updating +-.B name +-would change the UUID of an active subarray this operation is blocked, +-and the command will end in an error. ++option updates the subarray name in the metadata. It must be ++.BR "POSIX PORTABLE NAME" ++compatible and cannot be longer than 32 chars. If successes, new value will be respected after ++next assembly. + + The + .B ppl +@@ -3395,6 +3377,10 @@ When + .B \-\-incremental + mode is used, this file gets a list of arrays currently being created. + ++.SH POSIX PORTABLE NAME ++A valid name can only consist of characters "A-Za-z0-9.-_". ++The name cannot start with a leading "-" and cannot exceed 255 chars. ++ + .SH DEVICE NAMES + + .I mdadm +@@ -3416,6 +3402,10 @@ can be given, or just the suffix of the second sort of name, such as + .I home + can be given. + ++In every style, raw name must be compatible with ++.BR "POSIX PORTABLE NAME" ++and has to be no longer than 32 chars. ++ + When + .I mdadm + chooses device names during auto-assembly or incremental assembly, it +diff --git a/mdadm.conf.5.in b/mdadm.conf.5.in +index bc2295c2..94e23dd0 100644 +--- a/mdadm.conf.5.in ++++ b/mdadm.conf.5.in +@@ -717,10 +717,6 @@ ARRAY /dev/md/home UUID=9187a482:5dde19d9:eea3cc4a:d646ab8b + .br + auto=part + .br +-# The name of this array contains a space. +-.br +-ARRAY /dev/md9 name='Data Storage' +-.sp + POLICY domain=domain1 metadata=imsm path=pci-0000:00:1f.2-scsi-* + .br + action=spare +diff --git a/mdadm.h b/mdadm.h +index 49422e24..9effb941 100644 +--- a/mdadm.h ++++ b/mdadm.h +@@ -1617,6 +1617,7 @@ extern int check_raid(int fd, char *name); + extern int check_partitions(int fd, char *dname, + unsigned long long freesize, + unsigned long long size); ++extern bool is_name_posix_compatible(const char *path); + extern int fstat_is_blkdev(int fd, char *devname, dev_t *rdev); + extern int stat_is_blkdev(char *devname, dev_t *rdev); + +@@ -1657,7 +1658,6 @@ extern int conf_get_monitor_delay(void); + extern char *conf_line(FILE *file); + extern char *conf_word(FILE *file, int allow_key); + extern void print_quoted(char *str); +-extern void print_escape(char *str); + extern int use_udev(void); + extern unsigned long GCD(unsigned long a, unsigned long b); + extern int conf_name_is_free(char *name); +diff --git a/super-intel.c b/super-intel.c +index 77b0066f..05d3b056 100644 +--- a/super-intel.c ++++ b/super-intel.c +@@ -5561,40 +5561,37 @@ static void imsm_update_version_info(struct intel_super *super) + } + } + +-static int check_name(struct intel_super *super, char *name, int quiet) ++/** ++ * imsm_check_name() - check imsm naming criteria. ++ * @super: &intel_super pointer, not NULL. ++ * @name: name to check. ++ * @verbose: verbose level. ++ * ++ * Name must be no longer than &MAX_RAID_SERIAL_LEN and must be unique across volumes. ++ * ++ * Returns: &true if @name matches, &false otherwise. ++ */ ++static bool imsm_is_name_allowed(struct intel_super *super, const char * const name, ++ const int verbose) + { + struct imsm_super *mpb = super->anchor; +- char *reason = NULL; +- char *start = name; +- size_t len = strlen(name); + int i; + +- if (len > 0) { +- while (isspace(start[len - 1])) +- start[--len] = 0; +- while (*start && isspace(*start)) +- ++start, --len; +- memmove(name, start, len + 1); ++ if (is_string_lq(name, MAX_RAID_SERIAL_LEN + 1) == false) { ++ pr_vrb("imsm: Name \"%s\" is too long\n", name); ++ return false; + } + +- if (len > MAX_RAID_SERIAL_LEN) +- reason = "must be 16 characters or less"; +- else if (len == 0) +- reason = "must be a non-empty string"; +- + for (i = 0; i < mpb->num_raid_devs; i++) { + struct imsm_dev *dev = get_imsm_dev(super, i); + + if (strncmp((char *) dev->volume, name, MAX_RAID_SERIAL_LEN) == 0) { +- reason = "already exists"; +- break; ++ pr_vrb("imsm: Name \"%s\" already exists\n", name); ++ return false; + } + } + +- if (reason && !quiet) +- pr_err("imsm volume name %s\n", reason); +- +- return !reason; ++ return true; + } + + static int init_super_imsm_volume(struct supertype *st, mdu_array_info_t *info, +@@ -5689,8 +5686,9 @@ static int init_super_imsm_volume(struct supertype *st, mdu_array_info_t *info, + } + } + +- if (!check_name(super, name, 0)) ++ if (imsm_is_name_allowed(super, name, 1) == false) + return 0; ++ + dv = xmalloc(sizeof(*dv)); + dev = xcalloc(1, sizeof(*dev) + sizeof(__u32) * (info->raid_disks - 1)); + /* +@@ -8018,7 +8016,7 @@ static int update_subarray_imsm(struct supertype *st, char *subarray, + char *ep; + int vol; + +- if (!check_name(super, name, 0)) ++ if (imsm_is_name_allowed(super, name, 1) == false) + return 2; + + vol = strtoul(subarray, &ep, 10); +@@ -10345,7 +10343,8 @@ static void imsm_process_update(struct supertype *st, + if (a->info.container_member == target) + break; + dev = get_imsm_dev(super, u->dev_idx); +- if (a || !check_name(super, name, 1)) { ++ ++ if (a || !dev || imsm_is_name_allowed(super, name, 0) == false) { + dprintf("failed to rename subarray-%d\n", target); + break; + } +diff --git a/tests/00confnames b/tests/00confnames +index 25a7127b..10823f01 100644 +--- a/tests/00confnames ++++ b/tests/00confnames +@@ -79,10 +79,10 @@ names_verify "/dev/md127" "name" "name" + mdadm -S "/dev/md127" + + # 11. with some special symbols and locales, no . +-# It needs to wait a while for timeout because udev cannot create a link - known issue. ++# should be ignored. + names_make_conf $_UUID "tźż-\.,<>st+-" "empty" $config + mdadm -I $dev0 --config=$config +-names_verify "/dev/md127" "tźż-\.,<>st+-" "name" ++names_verify "/dev/md127" "name" "name" + mdadm -S "/dev/md127" + + # 12. No and set. +-- +2.40.1 + diff --git a/0158-Incremental-remove-obsoleted-calls-to-udisks.patch b/0158-Incremental-remove-obsoleted-calls-to-udisks.patch new file mode 100644 index 0000000..68e1e6d --- /dev/null +++ b/0158-Incremental-remove-obsoleted-calls-to-udisks.patch @@ -0,0 +1,128 @@ +From ba489abd688bf70c83e70700aaaec5f5e90889c5 Mon Sep 17 00:00:00 2001 +From: Coly Li +Date: Mon, 14 Aug 2023 00:46:13 +0800 +Subject: [PATCH 158/165] Incremental: remove obsoleted calls to udisks + +Utility udisks is removed from udev upstream, calling this obsoleted +command in run_udisks() doesn't make any sense now. + +This patch removes the calls chain of udisks, which includes routines +run_udisk(), force_remove(), and 2 locations where force_remove() are +called. Considering force_remove() is removed with udisks util, it is +fair to remove Manage_stop() inside force_remove() as well. + +In the two modifications where calling force_remove() are removed, +the failure from Manage_subdevs() can be safely ignored, because, +1) udisks doesn't exist, no need to check the return value to umount + the file system by udisks and remove the component disk again. +2) After the 'I' inremental remove, there is another 'r' hot remove + following up. The first incremental remove is a best-try effort. + +Therefore in this patch, where force_remove() is removed, the return +value of calling Manage_subdevs() is not checked too. + +Signed-off-by: Coly Li +Reviewed-by: Mariusz Tkaczyk +Cc: Jes Sorensen +Signed-off-by: Jes Sorensen +--- + Incremental.c | 64 +++++++++++---------------------------------------- + 1 file changed, 13 insertions(+), 51 deletions(-) + +diff --git a/Incremental.c b/Incremental.c +index f13ce027..05b33c45 100644 +--- a/Incremental.c ++++ b/Incremental.c +@@ -1628,54 +1628,18 @@ release: + return rv; + } + +-static void run_udisks(char *arg1, char *arg2) +-{ +- int pid = fork(); +- int status; +- if (pid == 0) { +- manage_fork_fds(1); +- execl("/usr/bin/udisks", "udisks", arg1, arg2, NULL); +- execl("/bin/udisks", "udisks", arg1, arg2, NULL); +- exit(1); +- } +- while (pid > 0 && wait(&status) != pid) +- ; +-} +- +-static int force_remove(char *devnm, int fd, struct mdinfo *mdi, int verbose) +-{ +- int rv; +- int devid = devnm2devid(devnm); +- +- run_udisks("--unmount", map_dev(major(devid), minor(devid), 0)); +- rv = Manage_stop(devnm, fd, verbose, 1); +- if (rv) { +- /* At least we can try to trigger a 'remove' */ +- sysfs_uevent(mdi, "remove"); +- if (verbose) +- pr_err("Fail to stop %s too.\n", devnm); +- } +- return rv; +-} +- + static void remove_from_member_array(struct mdstat_ent *memb, + struct mddev_dev *devlist, int verbose) + { +- int rv; +- struct mdinfo mmdi; + int subfd = open_dev(memb->devnm); + + if (subfd >= 0) { +- rv = Manage_subdevs(memb->devnm, subfd, devlist, verbose, +- 0, UOPT_UNDEFINED, 0); +- if (rv & 2) { +- if (sysfs_init(&mmdi, -1, memb->devnm)) +- pr_err("unable to initialize sysfs for: %s\n", +- memb->devnm); +- else +- force_remove(memb->devnm, subfd, &mmdi, +- verbose); +- } ++ /* ++ * Ignore the return value because it's necessary ++ * to handle failure condition here. ++ */ ++ Manage_subdevs(memb->devnm, subfd, devlist, verbose, ++ 0, UOPT_UNDEFINED, 0); + close(subfd); + } + } +@@ -1758,21 +1722,19 @@ int IncrementalRemove(char *devname, char *id_path, int verbose) + } + free_mdstat(mdstat); + } else { +- rv |= Manage_subdevs(ent->devnm, mdfd, &devlist, +- verbose, 0, UOPT_UNDEFINED, 0); +- if (rv & 2) { +- /* Failed due to EBUSY, try to stop the array. +- * Give udisks a chance to unmount it first. ++ /* ++ * This 'I' incremental remove is a try-best effort, ++ * the failure condition can be safely ignored ++ * because of the following up 'r' remove. + */ +- rv = force_remove(ent->devnm, mdfd, &mdi, verbose); +- goto end; +- } ++ Manage_subdevs(ent->devnm, mdfd, &devlist, ++ verbose, 0, UOPT_UNDEFINED, 0); + } + + devlist.disposition = 'r'; + rv = Manage_subdevs(ent->devnm, mdfd, &devlist, + verbose, 0, UOPT_UNDEFINED, 0); +-end: ++ + close(mdfd); + free_mdstat(ent); + return rv; +-- +2.40.1 + diff --git a/0159-mdadm-tests-Fix-regular-expression-failure.patch b/0159-mdadm-tests-Fix-regular-expression-failure.patch new file mode 100644 index 0000000..1493294 --- /dev/null +++ b/0159-mdadm-tests-Fix-regular-expression-failure.patch @@ -0,0 +1,31 @@ +From 28fc84168646910a9271ebe1a12b1571e14f5900 Mon Sep 17 00:00:00 2001 +From: Xiao Ni +Date: Thu, 7 Sep 2023 16:57:44 +0800 +Subject: [PATCH 159/165] mdadm/tests: Fix regular expression failure + +The test fails because of the regular expression. + +Signed-off-by: Xiao Ni +Signed-off-by: Jes Sorensen +--- + tests/06name | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/tests/06name b/tests/06name +index 4d5e824d..86eaab69 100644 +--- a/tests/06name ++++ b/tests/06name +@@ -3,8 +3,8 @@ set -x + # create an array with a name + + mdadm -CR $md0 -l0 -n2 --metadata=1 --name="Fred" $dev0 $dev1 +-mdadm -E $dev0 | grep 'Name : [^:]*:Fred ' > /dev/null || exit 1 +-mdadm -D $md0 | grep 'Name : [^:]*:Fred ' > /dev/null || exit 1 ++mdadm -E $dev0 | grep 'Name : Fred' > /dev/null || exit 1 ++mdadm -D $md0 | grep 'Name : Fred' > /dev/null || exit 1 + mdadm -S $md0 + + mdadm -A $md0 --name="Fred" $devlist +-- +2.40.1 + diff --git a/0160-Fix-race-of-mdadm-add-and-mdadm-incremental.patch b/0160-Fix-race-of-mdadm-add-and-mdadm-incremental.patch new file mode 100644 index 0000000..311f30a --- /dev/null +++ b/0160-Fix-race-of-mdadm-add-and-mdadm-incremental.patch @@ -0,0 +1,141 @@ +From ed2c2cb3d440f3bd2692f1896446c4bcc703f05c Mon Sep 17 00:00:00 2001 +From: Li Xiao Keng +Date: Thu, 7 Sep 2023 19:37:44 +0800 +Subject: [PATCH 160/165] Fix race of "mdadm --add" and "mdadm --incremental" +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +There is a raid1 with sda and sdb. And we add sdc to this raid, +it may return -EBUSY. + +The main process of --add: +1. dev_open(sdc) in Manage_add +2. store_super1(st, di->fd) in write_init_super1 +3. fsync(fd) in store_super1 +4. close(di->fd) in write_init_super1 +5. ioctl(ADD_NEW_DISK) + +Step 2 and 3 will add sdc to metadata of raid1. There will be +udev(change of sdc) event after step4. Then "/usr/sbin/mdadm +--incremental --export $devnode --offroot $env{DEVLINKS}" +will be run, and the sdc will be added to the raid1. Then +step 5 will return -EBUSY because it checks if device isn't +claimed in md_import_device()->lock_rdev()->blkdev_get_by_dev() +->blkdev_get(). + +It will be confusing for users because sdc is added first time. +The "incremental" will get map_lock before add sdc to raid1. +So we add map_lock before write_init_super in "mdadm --add" +to fix the race of "add" and "incremental". + +Signed-off-by: Li Xiao Keng +Signed-off-by: Guanqin Miao +Reviewed-by: Mariusz Tkaczyk +Signed-off-by: Jes Sorensen +--- + Manage.c | 24 ++++++++++++++++-------- + 1 file changed, 16 insertions(+), 8 deletions(-) + +diff --git a/Manage.c b/Manage.c +index f997b163..075dd720 100644 +--- a/Manage.c ++++ b/Manage.c +@@ -704,6 +704,7 @@ int Manage_add(int fd, int tfd, struct mddev_dev *dv, + struct supertype *dev_st; + int j; + mdu_disk_info_t disc; ++ struct map_ent *map = NULL; + + if (!get_dev_size(tfd, dv->devname, &ldsize)) { + if (dv->disposition == 'M') +@@ -907,6 +908,9 @@ int Manage_add(int fd, int tfd, struct mddev_dev *dv, + disc.raid_disk = 0; + } + ++ if (map_lock(&map)) ++ pr_err("failed to get exclusive lock on mapfile when add disk\n"); ++ + if (array->not_persistent==0) { + int dfd; + if (dv->disposition == 'j') +@@ -918,9 +922,9 @@ int Manage_add(int fd, int tfd, struct mddev_dev *dv, + dfd = dev_open(dv->devname, O_RDWR | O_EXCL|O_DIRECT); + if (tst->ss->add_to_super(tst, &disc, dfd, + dv->devname, INVALID_SECTORS)) +- return -1; ++ goto unlock; + if (tst->ss->write_init_super(tst)) +- return -1; ++ goto unlock; + } else if (dv->disposition == 'A') { + /* this had better be raid1. + * As we are "--re-add"ing we must find a spare slot +@@ -978,14 +982,14 @@ int Manage_add(int fd, int tfd, struct mddev_dev *dv, + pr_err("add failed for %s: could not get exclusive access to container\n", + dv->devname); + tst->ss->free_super(tst); +- return -1; ++ goto unlock; + } + + /* Check if metadata handler is able to accept the drive */ + if (!tst->ss->validate_geometry(tst, LEVEL_CONTAINER, 0, 1, NULL, + 0, 0, dv->devname, NULL, 0, 1)) { + close(container_fd); +- return -1; ++ goto unlock; + } + + Kill(dv->devname, NULL, 0, -1, 0); +@@ -994,7 +998,7 @@ int Manage_add(int fd, int tfd, struct mddev_dev *dv, + dv->devname, INVALID_SECTORS)) { + close(dfd); + close(container_fd); +- return -1; ++ goto unlock; + } + if (!mdmon_running(tst->container_devnm)) + tst->ss->sync_metadata(tst); +@@ -1005,7 +1009,7 @@ int Manage_add(int fd, int tfd, struct mddev_dev *dv, + dv->devname); + close(container_fd); + tst->ss->free_super(tst); +- return -1; ++ goto unlock; + } + sra->array.level = LEVEL_CONTAINER; + /* Need to set data_offset and component_size */ +@@ -1020,7 +1024,7 @@ int Manage_add(int fd, int tfd, struct mddev_dev *dv, + pr_err("add new device to external metadata failed for %s\n", dv->devname); + close(container_fd); + sysfs_free(sra); +- return -1; ++ goto unlock; + } + ping_monitor(devnm); + sysfs_free(sra); +@@ -1034,7 +1038,7 @@ int Manage_add(int fd, int tfd, struct mddev_dev *dv, + else + pr_err("add new device failed for %s as %d: %s\n", + dv->devname, j, strerror(errno)); +- return -1; ++ goto unlock; + } + if (dv->disposition == 'j') { + pr_err("Journal added successfully, making %s read-write\n", devname); +@@ -1045,7 +1049,11 @@ int Manage_add(int fd, int tfd, struct mddev_dev *dv, + } + if (verbose >= 0) + pr_err("added %s\n", dv->devname); ++ map_unlock(&map); + return 1; ++unlock: ++ map_unlock(&map); ++ return -1; + } + + int Manage_remove(struct supertype *tst, int fd, struct mddev_dev *dv, +-- +2.40.1 + diff --git a/0161-mdadm-tests-Don-t-run-mknod-before-losetup.patch b/0161-mdadm-tests-Don-t-run-mknod-before-losetup.patch new file mode 100644 index 0000000..7d00a5b --- /dev/null +++ b/0161-mdadm-tests-Don-t-run-mknod-before-losetup.patch @@ -0,0 +1,32 @@ +From c32b4754014c1eec8cc0c025fad2e9b621486164 Mon Sep 17 00:00:00 2001 +From: Xiao Ni +Date: Fri, 8 Sep 2023 16:44:35 +0800 +Subject: [PATCH 161/165] mdadm/tests: Don't run mknod before losetup + +Sometimes it can fail: +losetup: /var/tmp/mdtest0: failed to set up loop device: No such device or address +/dev/loop0 and /var/tmp/mdtest0 are already created before losetup. + +Because losetup can create device node by itself. So remove mknod. + +Signed-off-by: Xiao Ni +Signed-off-by: Jes Sorensen +--- + tests/func.sh | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/tests/func.sh b/tests/func.sh +index 9710a53b..5053b012 100644 +--- a/tests/func.sh ++++ b/tests/func.sh +@@ -170,7 +170,6 @@ do_setup() { + dd if=/dev/zero of=$targetdir/mdtest$d count=$sz bs=1K > /dev/null 2>&1 + # make sure udev doesn't touch + mdadm --zero $targetdir/mdtest$d 2> /dev/null +- [ -b /dev/loop$d ] || mknod /dev/loop$d b 7 $d + if [ $d -eq 7 ] + then + losetup /dev/loop$d $targetdir/mdtest6 # for multipath use +-- +2.40.1 + diff --git a/0162-mdadm-ddf-Abort-when-raid-disk-is-smaller-in-getinfo.patch b/0162-mdadm-ddf-Abort-when-raid-disk-is-smaller-in-getinfo.patch new file mode 100644 index 0000000..9c9ad6b --- /dev/null +++ b/0162-mdadm-ddf-Abort-when-raid-disk-is-smaller-in-getinfo.patch @@ -0,0 +1,45 @@ +From 246b4b40558f6f7f84882e9c34659f1b582944e2 Mon Sep 17 00:00:00 2001 +From: Xiao Ni +Date: Wed, 11 Oct 2023 21:03:32 +0800 +Subject: [PATCH 162/165] mdadm/ddf: Abort when raid disk is smaller in + getinfo_super_ddf +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The metadata is corrupted when the raid_disk<0. So abort directly. +This also can avoid a building error: +super-ddf.c:1988:58: error: array subscript -1 is below array bounds of ‘struct phys_disk_entry[0]’ + +Suggested-by: Mariusz Tkaczyk +Ackedy-by: Xiao Ni +Acked-by: Mariusz Tkaczyk +Signed-off-by: Jes Sorensen +--- + super-ddf.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/super-ddf.c b/super-ddf.c +index c5242654..7571e3b7 100644 +--- a/super-ddf.c ++++ b/super-ddf.c +@@ -1984,12 +1984,14 @@ static void getinfo_super_ddf(struct supertype *st, struct mdinfo *info, char *m + info->disk.number = be32_to_cpu(ddf->dlist->disk.refnum); + info->disk.raid_disk = find_phys(ddf, ddf->dlist->disk.refnum); + ++ if (info->disk.raid_disk < 0) ++ return; ++ + info->data_offset = be64_to_cpu(ddf->phys-> + entries[info->disk.raid_disk]. + config_size); + info->component_size = ddf->dlist->size - info->data_offset; +- if (info->disk.raid_disk >= 0) +- pde = ddf->phys->entries + info->disk.raid_disk; ++ pde = ddf->phys->entries + info->disk.raid_disk; + if (pde && + !(be16_to_cpu(pde->state) & DDF_Failed) && + !(be16_to_cpu(pde->state) & DDF_Missing)) +-- +2.40.1 + diff --git a/0163-mdadm-super1-Add-MD_FEATURE_RAID0_LAYOUT-if-kernel-5.patch b/0163-mdadm-super1-Add-MD_FEATURE_RAID0_LAYOUT-if-kernel-5.patch new file mode 100644 index 0000000..f31d4c6 --- /dev/null +++ b/0163-mdadm-super1-Add-MD_FEATURE_RAID0_LAYOUT-if-kernel-5.patch @@ -0,0 +1,75 @@ +From 6af520a5b4d91cd0fd32ab6361ff4519505c7f47 Mon Sep 17 00:00:00 2001 +From: Xiao Ni +Date: Tue, 17 Oct 2023 20:35:46 +0800 +Subject: [PATCH 163/165] mdadm/super1: Add MD_FEATURE_RAID0_LAYOUT if + kernel>=5.4 + +After and include kernel v5.4, it adds one feature bit MD_FEATURE_RAID0_LAYOUT. +It must need to specify a layout for raid0 with more than one zone. But for +raid0 with one zone, in fact it also has a defalut layout. + +Now for raid0 with one zone, *unknown* layout can be seen when running mdadm -D +command. It's the reason that mdadm doesn't set MD_FEATURE_RAID0_LAYOUT for +raid0 with one zone. Then in kernel space, super_1_validate sets mddev->layout +to -1 because of no MD_FEATURE_RAID0_LAYOUT. In fact, in raid0 io path, it +uses the default layout. Set raid0_need_layout to true if kernel_version<=v5.4. + +Fixes: 329dfc28debb ('Create: add support for RAID0 layouts.') +Signed-off-by: Xiao Ni +Reviewed-by: Mariusz Tkaczyk +Signed-off-by: Jes Sorensen +--- + super1.c | 19 ++++++++++++++++--- + 1 file changed, 16 insertions(+), 3 deletions(-) + +diff --git a/super1.c b/super1.c +index 856b0208..1da71b98 100644 +--- a/super1.c ++++ b/super1.c +@@ -1967,6 +1967,14 @@ fail_to_write: + return 1; + } + ++static bool has_raid0_layout(struct mdp_superblock_1 *sb) ++{ ++ if (sb->level == 0 && sb->layout != 0) ++ return true; ++ else ++ return false; ++} ++ + static int write_init_super1(struct supertype *st) + { + struct mdp_superblock_1 *sb = st->sb; +@@ -1978,12 +1986,17 @@ static int write_init_super1(struct supertype *st) + unsigned long long sb_offset; + unsigned long long data_offset; + long bm_offset; +- int raid0_need_layout = 0; ++ bool raid0_need_layout = false; ++ ++ /* Since linux kernel v5.4, raid0 always has a layout */ ++ if (has_raid0_layout(sb) && get_linux_version() >= 5004000) ++ raid0_need_layout = true; + + for (di = st->info; di; di = di->next) { + if (di->disk.state & (1 << MD_DISK_JOURNAL)) + sb->feature_map |= __cpu_to_le32(MD_FEATURE_JOURNAL); +- if (sb->level == 0 && sb->layout != 0) { ++ if (has_raid0_layout(sb) && !raid0_need_layout) { ++ + struct devinfo *di2 = st->info; + unsigned long long s1, s2; + s1 = di->dev_size; +@@ -1995,7 +2008,7 @@ static int write_init_super1(struct supertype *st) + s2 -= di2->data_offset; + s2 /= __le32_to_cpu(sb->chunksize); + if (s1 != s2) +- raid0_need_layout = 1; ++ raid0_need_layout = true; + } + } + +-- +2.40.1 + diff --git a/0164-mdadm-remove-container_enough-logic.patch b/0164-mdadm-remove-container_enough-logic.patch new file mode 100644 index 0000000..2077aab --- /dev/null +++ b/0164-mdadm-remove-container_enough-logic.patch @@ -0,0 +1,139 @@ +From 4dde420fc3e24077ab926f79674eaae1b71de10b Mon Sep 17 00:00:00 2001 +From: Pawel Piatkowski +Date: Thu, 19 Oct 2023 16:35:24 +0200 +Subject: [PATCH 164/165] mdadm: remove container_enough logic + +Arrays without enough disk count will be assembled but not +started. +Now RAIDs will be assembled always (even if they are failed). +RAID devices in all states will be assembled and exposed +to mdstat. +This change affects only IMSM (for ddf it wasn't used, +container_enough was set to true always). +Removed this logic from incremental_container as well with +runstop checking because runstop condition is being verified +in assemble_container_content function. + +Signed-off-by: Pawel Piatkowski +Signed-off-by: Jes Sorensen +--- + Incremental.c | 11 ----------- + mdadm.h | 3 --- + super-ddf.c | 1 - + super-intel.c | 32 +------------------------------- + 4 files changed, 1 insertion(+), 46 deletions(-) + +diff --git a/Incremental.c b/Incremental.c +index 05b33c45..3551c65b 100644 +--- a/Incremental.c ++++ b/Incremental.c +@@ -1467,17 +1467,6 @@ static int Incremental_container(struct supertype *st, char *devname, + + st->ss->getinfo_super(st, &info, NULL); + +- if ((c->runstop > 0 && info.container_enough >= 0) || +- info.container_enough > 0) +- /* pass */; +- else { +- if (c->export) { +- printf("MD_STARTED=no\n"); +- } else if (c->verbose) +- pr_err("not enough devices to start the container\n"); +- return 0; +- } +- + match = conf_match(st, &info, devname, c->verbose, &rv); + if (match == NULL && rv == 2) + return rv; +diff --git a/mdadm.h b/mdadm.h +index 9effb941..b48e6f86 100644 +--- a/mdadm.h ++++ b/mdadm.h +@@ -377,9 +377,6 @@ struct mdinfo { + int container_member; /* for assembling external-metatdata arrays + * This is to be used internally by metadata + * handler only */ +- int container_enough; /* flag external handlers can set to +- * indicate that subarrays have not enough (-1), +- * enough to start (0), or all expected disks (1) */ + char sys_name[32]; + struct mdinfo *devs; + struct mdinfo *next; +diff --git a/super-ddf.c b/super-ddf.c +index 7571e3b7..a87e3169 100644 +--- a/super-ddf.c ++++ b/super-ddf.c +@@ -1975,7 +1975,6 @@ static void getinfo_super_ddf(struct supertype *st, struct mdinfo *info, char *m + info->array.ctime = DECADE + __be32_to_cpu(*cptr); + + info->array.chunk_size = 0; +- info->container_enough = 1; + + info->disk.major = 0; + info->disk.minor = 0; +diff --git a/super-intel.c b/super-intel.c +index 05d3b056..6bdd5c4c 100644 +--- a/super-intel.c ++++ b/super-intel.c +@@ -3806,7 +3806,6 @@ static void getinfo_super_imsm(struct supertype *st, struct mdinfo *info, char * + struct intel_super *super = st->sb; + struct imsm_disk *disk; + int map_disks = info->array.raid_disks; +- int max_enough = -1; + int i; + struct imsm_super *mpb; + +@@ -3848,12 +3847,9 @@ static void getinfo_super_imsm(struct supertype *st, struct mdinfo *info, char * + + for (i = 0; i < mpb->num_raid_devs; i++) { + struct imsm_dev *dev = get_imsm_dev(super, i); +- int failed, enough, j, missing = 0; ++ int j = 0; + struct imsm_map *map; +- __u8 state; + +- failed = imsm_count_failed(super, dev, MAP_0); +- state = imsm_check_degraded(super, dev, failed, MAP_0); + map = get_imsm_map(dev, MAP_0); + + /* any newly missing disks? +@@ -3868,36 +3864,10 @@ static void getinfo_super_imsm(struct supertype *st, struct mdinfo *info, char * + + if (!(ord & IMSM_ORD_REBUILD) && + get_imsm_missing(super, idx)) { +- missing = 1; + break; + } + } +- +- if (state == IMSM_T_STATE_FAILED) +- enough = -1; +- else if (state == IMSM_T_STATE_DEGRADED && +- (state != map->map_state || missing)) +- enough = 0; +- else /* we're normal, or already degraded */ +- enough = 1; +- if (is_gen_migration(dev) && missing) { +- /* during general migration we need all disks +- * that process is running on. +- * No new missing disk is allowed. +- */ +- max_enough = -1; +- enough = -1; +- /* no more checks necessary +- */ +- break; +- } +- /* in the missing/failed disk case check to see +- * if at least one array is runnable +- */ +- max_enough = max(max_enough, enough); + } +- dprintf("enough: %d\n", max_enough); +- info->container_enough = max_enough; + + if (super->disks) { + __u32 reserved = imsm_reserved_sectors(super, super->disks); +-- +2.40.1 + diff --git a/0165-Fix-assembling-RAID-volume-by-using-incremental.patch b/0165-Fix-assembling-RAID-volume-by-using-incremental.patch new file mode 100644 index 0000000..0db1767 --- /dev/null +++ b/0165-Fix-assembling-RAID-volume-by-using-incremental.patch @@ -0,0 +1,49 @@ +From d8d09c1633b2f06f88633ab960aa02b41a6bdfb6 Mon Sep 17 00:00:00 2001 +From: Pawel Piatkowski +Date: Thu, 19 Oct 2023 16:35:25 +0200 +Subject: [PATCH 165/165] Fix assembling RAID volume by using incremental + +After change "mdadm: remove container_enough logic" +IMSM volumes are started immediately. If volume is during +reshape, then it will be blocked by block_subarray() during +first mdadm -I . Assemble_container_content() for +next disk will see the change because metadata version from +sysfs and metadata doesn't match and will execute +sysfs_set_array again. Then it fails to set same +component_size, it is prohibited by kernel. + +If array is frozen then first sign from metadata version +is different ("/" vs "-"), so exclude it from comparison. +All we want is to double check that base properties are set +and we don't need to call sysfs_set_array again. + +Signed-off-by: Pawel Piatkowski +Signed-off-by: Jes Sorensen +--- + Assemble.c | 10 ++++------ + 1 file changed, 4 insertions(+), 6 deletions(-) + +diff --git a/Assemble.c b/Assemble.c +index 5be58e40..0557a007 100644 +--- a/Assemble.c ++++ b/Assemble.c +@@ -1990,12 +1990,10 @@ int assemble_container_content(struct supertype *st, int mdfd, + return 1; + } + +- if (strcmp(sra->text_version, content->text_version) != 0) { +- if (content->array.major_version == -1 && +- content->array.minor_version == -2 && +- c->readonly && +- content->text_version[0] == '/') +- content->text_version[0] = '-'; ++ /* Fill sysfs properties only if they are not set. Determine it by checking text_version ++ * and ignoring special character on the first place. ++ */ ++ if (strcmp(sra->text_version + 1, content->text_version + 1) != 0) { + if (sysfs_set_array(content, 9003) != 0) { + sysfs_free(sra); + return 1; +-- +2.40.1 + diff --git a/disable-Werror.patch b/disable-Werror.patch deleted file mode 100644 index 2d52c67..0000000 --- a/disable-Werror.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- mdadm/Makefile.orig 2021-07-28 21:39:23.887433859 +0800 -+++ mdadm/Makefile 2021-07-28 21:39:37.989432841 +0800 -@@ -50,7 +50,7 @@ - CC := $(CROSS_COMPILE)gcc - endif - CXFLAGS ?= -ggdb --CWFLAGS = -Wall -Werror -Wstrict-prototypes -Wextra -Wno-unused-parameter -+CWFLAGS = -Wall -Wstrict-prototypes -Wextra -Wno-unused-parameter - ifdef WARN_UNUSED - CWFLAGS += -Wp,-D_FORTIFY_SOURCE=2 -O3 - endif diff --git a/mdadm.spec b/mdadm.spec index e0f41ff..9795037 100644 --- a/mdadm.spec +++ b/mdadm.spec @@ -1,7 +1,7 @@ Name: mdadm Version: 4.2 # extraversion is used to define rhel internal version -%define extraversion 9 +%define extraversion 10 Release: %{extraversion}%{?dist} Summary: The mdadm program controls Linux md devices (software RAID arrays) URL: http://www.kernel.org/pub/linux/utils/raid/mdadm/ @@ -144,13 +144,51 @@ Patch121: 0122-mdadm-define-DEV_NUM_PREF.patch Patch122: 0123-mdadm-define-is_devname_ignore.patch Patch123: 0124-mdadm-numbered-names-verification.patch Patch124: 0125-enable-RAID-for-SATA-under-VMD.patch +Patch125: 0126-imsm-Fix-possible-segfault-in-check_no_platform.patch +Patch126: 0127-imsm-move-sum_extents-calculations-to-merge_extents.patch +Patch127: 0128-imsm-imsm_get_free_size-refactor.patch +Patch128: 0129-imsm-introduce-round_member_size_to_mb.patch +Patch129: 0130-imsm-move-expand-verification-code-into-new-function.patch +Patch130: 0131-imsm-return-free-space-after-volume-for-expand.patch +Patch131: 0132-imsm-fix-free-space-calculations.patch +Patch132: 0133-Add-secure-gethostname-wrapper.patch +Patch133: 0134-mdadm-Stop-mdcheck_continue-timer-when-mdcheck_start.patch +Patch134: 0135-Fix-memory-leak-in-file-Assemble.patch +Patch135: 0136-Fix-memory-leak-in-file-Kill.patch +Patch136: 0137-Fix-memory-leak-in-file-Manage.patch +Patch137: 0138-Fix-memory-leak-in-file-mdadm.patch +Patch138: 0139-Fix-unsafe-string-functions.patch +Patch139: 0140-platform-intel-limit-guid-length.patch +Patch140: 0141-imsm-Add-reading-vmd-register-for-finding-imsm-capab.patch +Patch141: 0142-Add-compiler-defenses-flags.patch +Patch142: 0143-Assemble-fix-redundant-memory-free.patch +Patch143: 0144-tests-add-a-new-test-for-rdev-lifetime.patch +Patch144: 0145-tests-support-to-skip-checking-dmesg.patch +Patch145: 0146-tests-add-a-regression-test-for-raid10-deadlock.patch +Patch146: 0147-tests-add-a-regression-test-for-raid456-deadlock.patch +Patch147: 0148-tests-add-a-regression-test-that-raid456-can-t-assem.patch +Patch148: 0149-tests-add-a-regression-test-that-raid456-can-t-assem.patch +Patch149: 0150-tests-add-a-regression-test-that-reshape-can-corrupt.patch +Patch150: 0151-tests-add-a-regression-test-for-raid456-deadlock-aga.patch +Patch151: 0152-tests-create-names_template.patch +Patch152: 0153-tests-create-00confnames.patch +Patch153: 0154-mdadm-set-ident.devname-if-applicable.patch +Patch154: 0155-mdadm-refactor-ident-name-handling.patch +Patch155: 0156-mdadm-define-ident_set_devname.patch +Patch156: 0157-mdadm-Follow-POSIX-Portable-Character-Set.patch +Patch157: 0158-Incremental-remove-obsoleted-calls-to-udisks.patch +Patch158: 0159-mdadm-tests-Fix-regular-expression-failure.patch +Patch159: 0160-Fix-race-of-mdadm-add-and-mdadm-incremental.patch +Patch160: 0161-mdadm-tests-Don-t-run-mknod-before-losetup.patch +Patch161: 0162-mdadm-ddf-Abort-when-raid-disk-is-smaller-in-getinfo.patch +Patch162: 0163-mdadm-super1-Add-MD_FEATURE_RAID0_LAYOUT-if-kernel-5.patch +Patch163: 0164-mdadm-remove-container_enough-logic.patch +Patch164: 0165-Fix-assembling-RAID-volume-by-using-incremental.patch # Fedora customization patches Patch200: mdadm-udev.patch Patch201: mdadm-2.5.2-static.patch -# Build without -Werror. -Patch202: disable-Werror.patch BuildRequires: make BuildRequires: systemd-rpm-macros binutils-devel gcc systemd-devel @@ -224,6 +262,10 @@ install -m644 %{SOURCE5} %{buildroot}/etc/libreport/events.d /usr/share/mdadm/mdcheck %changelog +* Fri Nov 3 2023 Xiao Ni - 4.2-10 +- Update to latest upstream +- Resolves RHEL-15386 + * Tue May 16 2023 Xiao Ni - 4.2-9 - Update to latest upstream and fix mdcheck service bug - Resolves rhbz#2159923, rhbz#2150865, rhbz#2124071, rhbz#2203859