1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 #include <meta.h>
  27 #include <assert.h>
  28 #include <ctype.h>
  29 #include <mdiox.h>
  30 #include <meta.h>
  31 #include <stdio.h>
  32 #include <stdlib.h>
  33 #include <strings.h>
  34 #include <sys/lvm/md_mddb.h>
  35 #include <sys/lvm/md_names.h>
  36 #include <sys/lvm/md_crc.h>
  37 #include <sys/lvm/md_convert.h>
  38 
  39 
  40 /*
  41  * Design Notes:
  42  *
  43  * All of the code in this file supports the addition of metastat -c output
  44  * for the verbose option of metaimport.  Some of this code is also used by
  45  * the command metastat for concise output(cmd/lvm/util/metastat.c).
  46  * The code is designed to produce the same output as metastat -c does for a
  47  * given diskset--with a couple exceptions.
  48  * The primary differences between the output for the metastat -c command and
  49  * metastat output for metaimport -v are:
  50  *  - the set name is not printed next to each metadevice
  51  *  - top-level state information is not printed for some metadevices
  52  *  - the percent that a disk has completed resyncing is not listed
  53  * in metaimport -v.
  54  *
  55  *
  56  * The general layout of this file is as follows:
  57  *
  58  *  - report_metastat_info()
  59  *      This is the primary entry point for the functions in this file, with
  60  *      the exception of several functions that are also called from
  61  *      cmd/io/lvm/util/metastat.c
  62  *      report_metastat_info() calls functions to read in all the the
  63  *      Directory blocks and Record blocks and then process the information
  64  *      needed to print out the metadevice records in the same format as
  65  *      metastat -c.
  66  *
  67  *  - read_all_mdrecords()
  68  *      Reads in all the Directory blocks in the diskset and verifies their
  69  *      validity.  For each Directly block, it loops through all Directory
  70  *      Entries and for each one that contains a metadevice record calls
  71  *      read_md_record().  Because the output is designed to imitate the
  72  *      output of metastat -c, we ignore metadevice records for
  73  *      optimized resync, changelog, and translog.
  74  *
  75  *  - read_md_record()
  76  *      Reads in a Directory Entry and its associated Record block.  The
  77  *      revision information for the Record block is checked and it is
  78  *      determined whether or not it is a 64bit Record block or a 32bit record
  79  *      block.  For each valid Record block, it allocates an md_im_rec_t
  80  *      structure and calls extract_mduser_data().
  81  *
  82  *  - extract_mduser_data()
  83  *      Populates the md_im_rec_t data structure with information about the
  84  *      record's associated metadevice.  Also, the name of the metadevice is
  85  *      either copied from the NM namespace(if it exists there) or is generated
  86  *      from the record's un_self_id.
  87  *
  88  *  - process_toplevel_devices()
  89  *      For a given metadevice type, searchs through the md_im_rec_t **mdimpp,
  90  *      list of all metadevices in the set, to find all records of the
  91  *      specified type that do not have a parent and puts them on a temp list.
  92  *      The temp list is then iterated through and the associated processing
  93  *      function is called.
  94  *
  95  *  - process_(trans, hotspare, hotspare_pool, soft_part, mirror, stripe, raid)
  96  *      These functions are called by using the dfunc field in the mdimpp list.
  97  *      Each process function only understands its own type of metadevice. Once
  98  *      it processes the metadevice it was called for, it then loops through
  99  *      all of the underlying metadevices.  After printing the name of the
 100  *      underlying metadevice, it puts in on a list to be processed.  If the
 101  *      underlying device is a physical device, then print_physical_device is
 102  *      called.
 103  *      Once all information about the original metadevice is processed, it
 104  *      loops through the list of underlying metadevices and calls the
 105  *      appropriate function to process them.
 106  *
 107  *  - process_toplevel_softparts()
 108  *      To match the output for metastat -c, all top-level softpartions
 109  *      are printed out in groups based on their underlying metadevice--so that
 110  *      the underlying metadevice only needs to be processed once.
 111  *
 112  *  - meta_get_(sm_state, raid_col_state, stripe_state, hs_state)
 113  *      These functions are used to retrieve the metadevice state information.
 114  *      They are also used by the metastat concise routines in
 115  *      cmd/lvm/util/metastat.c.
 116  *
 117  */
 118 
 119 
 120 /*
 121  * md_im_rec is a doubly linked list used to store the rb_data for each
 122  * directory entry that corresponds to a metadevice.
 123  * n_key: is set, if there is an associated entry in the NM namespace.
 124  * dfunc: is set to point to the function that processes the particular
 125  * metadevice associated with the record.
 126  * hs_record_id: is only set, if the metadevice is a hotspare.
 127  * un_self_id: is set for all other records. This is also used to generate
 128  * the name of the metadevice if there is no entry for the metadevice in
 129  * the NM namespace--n_key is not set.
 130  */
 131 typedef struct md_im_rec {
 132         mdkey_t                 n_key; /* NM namespace key */
 133         struct md_im_rec        *next;
 134         struct md_im_rec        *prev;
 135         uint_t                  md_type;
 136         uint_t                  has_parent; /* either 0(no parent) or 1 */
 137         minor_t                 un_self_id;
 138         mddb_recid_t            hs_record_id; /* hotspare recid */
 139         char                    *n_name;  /* name of metadevice */
 140         void                    (*dfunc) ();
 141         ushort_t                record_len;
 142         /* pointer to the unit structure for the metadevice, e.g. rb_data[0] */
 143         void                    *record;
 144 } md_im_rec_t;
 145 
 146 /*
 147  * md_im_list is used to group toplevel metadevices by type and to group
 148  * the underlying devices for a particular metadevice.
 149  */
 150 typedef struct md_im_list {
 151         struct md_im_list       *next;
 152         struct md_im_rec        *mdrec;
 153 } md_im_list_t;
 154 
 155 
 156 /*
 157  * MAXSIZEMDRECNAME is the value that has historically been used to allocate
 158  * space for the metadevice name
 159  */
 160 #define MAXSIZEMDRECNAME        20
 161 #define NAMEWIDTH               16
 162 #if defined(__GNUC__)
 163 #define offsetof(s, m)  __builtin_offsetof(s, m)
 164 #else
 165 #define offsetof(s, m)  ((size_t)(&(((s *)0)->m)))
 166 #endif
 167 #define NOT_PHYSICAL_DEV        0
 168 #define PHYSICAL_DEV            1
 169 
 170 
 171 /*
 172  * strip_blacks()
 173  *
 174  * Strip blanks from string.  Used for size field in concise output.
 175  */
 176 static char *
 177 strip_blanks(char *s)
 178 {
 179         char *p;
 180 
 181         for (p = s; *p; ) {
 182                 if (*p == ' ') {
 183                         char *t;
 184                         for (t = p; *t; t++) {
 185                                 *t = *(t + 1);
 186                         }
 187                 } else {
 188                         p++;
 189                 }
 190         }
 191 
 192         return (s);
 193 }
 194 
 195 
 196 /*
 197  * print_concise_entry()
 198  *
 199  * Print properly indented metadevice name, type and size for concise output.
 200  * This function is also called from: cmd/lvm/util/metastat.c.
 201  */
 202 void
 203 print_concise_entry(int indent, char *name, diskaddr_t size, char mtype)
 204 {
 205         int     i;
 206         int     width = NAMEWIDTH;      /* minumum field width for name */
 207         char    in[MAXPATHLEN];
 208         char    *sz;
 209 
 210         in[0] = 0;
 211         for (i = 0; i < indent; i++)
 212                 (void) strlcat(in, " ", sizeof (in));
 213 
 214         /* set up minimum field width. negative for left justified */
 215         width -= indent;
 216         if (width < 0)
 217                 width = 0;      /* overflowed; no minimum field needed */
 218         else
 219                 width = 0 - width; /* negative for left justification */
 220 
 221         if (size == 0) {
 222                 sz = "-";
 223         } else {
 224                 sz = strip_blanks(meta_number_to_string(size, DEV_BSIZE));
 225         }
 226 
 227         (void) printf("%s%*s %c %6s", in, width, name, mtype, sz);
 228 }
 229 
 230 
 231 /*
 232  * free_mdrec_list_entry()
 233  *
 234  * Removing entry from the list of metadevices in the diskset(mdimpp).
 235  * This function will not remove the dummy entry at the head of the
 236  * list, so we don't have to set mdrec equal to NULL.
 237  */
 238 static void
 239 free_mdrec_list_entry(md_im_rec_t  **mdrec)
 240 {
 241         (*mdrec)->prev->next = (*mdrec)->next;
 242         if ((*mdrec)->next != NULL) {
 243                 (*mdrec)->next->prev = (*mdrec)->prev;
 244         }
 245         Free((*mdrec)->record);
 246         Free((*mdrec)->n_name);
 247         Free(*mdrec);
 248 }
 249 
 250 
 251 /*
 252  * ucomponent_append()
 253  *
 254  * Appending entry to the underlying component list.  The list
 255  * is used to group all of the underlying devices before
 256  * processing them.
 257  */
 258 static void
 259 ucomponent_append(
 260         md_im_list_t    **ucomp_head,
 261         md_im_list_t    **ucomp_tail,
 262         md_im_list_t    *ucomp
 263 )
 264 {
 265         ucomp->next = NULL;
 266         if (*ucomp_head == NULL) {
 267                 *ucomp_head = ucomp;
 268                 *ucomp_tail = ucomp;
 269         } else {
 270                 (*ucomp_tail)->next = ucomp;
 271                 *ucomp_tail = (*ucomp_tail)->next;
 272         }
 273 }
 274 
 275 
 276 /*
 277  * free_md_im_list_entries()
 278  *
 279  * Freeing entries on an md_im_list_t.  This list is used to group
 280  * underlying components for processing and to group top-level metadevices
 281  * by type.
 282  */
 283 static void
 284 free_md_im_list_entries(md_im_list_t **list_head)
 285 {
 286         md_im_list_t    *tmp_list_entry = *list_head;
 287         md_im_list_t    *rm_list_entry;
 288 
 289         while (tmp_list_entry != NULL) {
 290                 rm_list_entry = tmp_list_entry;
 291                 tmp_list_entry = tmp_list_entry->next;
 292                 Free(rm_list_entry);
 293         }
 294 }
 295 
 296 
 297 /*
 298  * print_physical_device()
 299  *
 300  * If a metadevice has an underlying component that is a physical
 301  * device, then this searches the pnm_rec_t list to match an entry's
 302  * n_key to the key for the underlying component.  The ctd name of the
 303  * physical device is printed on the same line as the metadevice.
 304  */
 305 static void
 306 print_physical_device(
 307         pnm_rec_t       *phys_nm,
 308         mdkey_t         key
 309 )
 310 {
 311         pnm_rec_t       *tmpphys_nm;
 312 
 313         for (tmpphys_nm = phys_nm; tmpphys_nm != NULL;
 314             tmpphys_nm = tmpphys_nm->next) {
 315                 if (tmpphys_nm->n_key == key) {
 316                         (void) printf(" %s", tmpphys_nm->n_name);
 317                         break;
 318                 }
 319         }
 320 }
 321 
 322 
 323 /*
 324  * get_stripe_req_size()
 325  *
 326  * Given a 64bit stripe unit, compute the size of the stripe unit.
 327  * This function is a derivation of:
 328  *      common/lvm/md_convert.c:get_big_stripe_req_size()
 329  * and any changes made to either this function or get_big_stripe_req_size()
 330  * should be reviewed to make sure the functionality in both places is correct.
 331  *
 332  * Returns:
 333  *      total size of the 64bit stripe
 334  */
 335 size_t
 336 get_stripe_req_size(ms_unit_t *un)
 337 {
 338         struct ms_row *mdr;
 339         uint_t row;
 340         uint_t ncomps = 0;
 341         size_t mdsize = 0;
 342         size_t first_comp = 0;
 343 
 344 
 345         /* Compute the offset of the first component */
 346         first_comp = sizeof (ms_unit_t) +
 347             sizeof (struct ms_row) * (un->un_nrows - 1);
 348         first_comp = roundup(first_comp, sizeof (long long));
 349 
 350         /*
 351          * Requestor wants to have the total size, add the sizes of
 352          * all components
 353          */
 354         mdr = &un->un_row[0];
 355         for (row = 0; (row < un->un_nrows); row++)
 356             ncomps += mdr[row].un_ncomp;
 357         mdsize = first_comp + sizeof (ms_comp_t) * ncomps;
 358         return (mdsize);
 359 }
 360 
 361 
 362 /*
 363  * meta_get_sm_state()
 364  *
 365  * Gets the state for the underlying components(submirrors) of a mirror.
 366  * This function is also called from: cmd/lvm/util/metastat.c.
 367  *
 368  * Returns:
 369  *      string for state of the sub-mirror
 370  */
 371 static char *
 372 meta_get_sm_state(
 373         sm_state_t      state
 374 )
 375 {
 376         /* all is well */
 377         if (state & SMS_RUNNING) {
 378                 return (NULL);
 379         }
 380 
 381         /* resyncing, needs repair */
 382         if ((state & (SMS_COMP_RESYNC | SMS_ATTACHED_RESYNC |
 383             SMS_OFFLINE_RESYNC))) {
 384                 return (gettext("resyncing"));
 385         }
 386 
 387         /* needs repair */
 388         if (state & (SMS_COMP_ERRED | SMS_ATTACHED | SMS_OFFLINE))
 389                 return (gettext("maint"));
 390 
 391         /* unknown */
 392         return (gettext("unknown"));
 393 }
 394 
 395 
 396 /*
 397  * meta_get_raid_col_state()
 398  *
 399  * Gets the state for the underlying components(columns) of a raid.
 400  * This function is also called from: cmd/lvm/util/metastat.c.
 401  *
 402  * Returns:
 403  *      string for state of the raid column
 404  *
 405  */
 406 char *
 407 meta_get_raid_col_state(
 408         rcs_state_t     state
 409 )
 410 {
 411         switch (state) {
 412                 case RCS_INIT:
 413                         return (gettext("initializing"));
 414                 case RCS_OKAY:
 415                         return (NULL);
 416                 case RCS_INIT_ERRED:
 417                         /*FALLTHROUGH*/
 418                 case RCS_ERRED:
 419                         return (gettext("maint"));
 420                 case RCS_LAST_ERRED:
 421                         return (gettext("last-erred"));
 422                 case RCS_RESYNC:
 423                         return (gettext("resyncing"));
 424                 default:
 425                         return (gettext("unknown"));
 426         }
 427 }
 428 
 429 
 430 /*
 431  * meta_get_stripe_state()
 432  *
 433  * Gets the state for the underlying components of a stripe.
 434  * This function is also called from: cmd/lvm/util/metastat.c.
 435  *
 436  * Returns:
 437  *      string for state of the stripe
 438  *
 439  */
 440 char *
 441 meta_get_stripe_state(
 442         comp_state_t    state
 443 )
 444 {
 445         switch (state) {
 446                 case CS_OKAY:
 447                         return (NULL);
 448                 case CS_ERRED:
 449                         return (gettext("maint"));
 450                 case CS_LAST_ERRED:
 451                         return (gettext("last-erred"));
 452                 case CS_RESYNC:
 453                         return (gettext("resyncing"));
 454                 default:
 455                         return (gettext("invalid"));
 456         }
 457 }
 458 
 459 
 460 /*
 461  * meta_get_hs_state()
 462  *
 463  * Gets the state for the underlying components(hotspares) of a hotspare pool.
 464  * This function is also called from: cmd/lvm/util/metastat.c.
 465  *
 466  * Returns:
 467  *      string for state of the hotspare
 468  *
 469  */
 470 char *
 471 meta_get_hs_state(
 472         hotspare_states_t       state
 473 )
 474 {
 475         switch (state) {
 476                 case HSS_AVAILABLE:
 477                         return (NULL);
 478                 case HSS_RESERVED:
 479                         return (gettext("in-use"));
 480                 case HSS_BROKEN:
 481                         return (gettext("broken"));
 482                 case HSS_UNUSED:
 483                         /* FALLTHROUGH */
 484                 default:
 485                         return (gettext("invalid"));
 486         }
 487 }
 488 
 489 
 490 /*
 491  * process_trans()
 492  *
 493  * Prints unit information for a trans metadevice and calls the respective
 494  * functions to process the underlying metadevices.
 495  *
 496  */
 497 static void
 498 process_trans(
 499         md_im_rec_t     **mdimpp,
 500         int             indent,
 501         pnm_rec_t       *phys_nm,
 502         md_im_rec_t     *mdrec
 503 )
 504 {
 505         mt_unit_t       *mt;
 506         mdc_unit_t      uc;
 507         md_im_rec_t     *tmpmdrec;
 508         int             underlying_device = PHYSICAL_DEV;
 509 
 510         mt = (mt_unit_t *)mdrec->record;
 511         uc = mt->c;
 512 
 513         /* Printing name, size, and type of metadevice */
 514         print_concise_entry(indent, mdrec->n_name,
 515             uc.un_total_blocks, 't');
 516 
 517         /*
 518          * Loops through md_im_rec_t **mdimpp list of all metadevices to find
 519          * record that matches the underlying device.
 520          * Trans devices can only have one underlying device, so once a
 521          * match is found, we are done.
 522          */
 523         for (tmpmdrec = *mdimpp; tmpmdrec != NULL;
 524             tmpmdrec = tmpmdrec->next) {
 525                 if (tmpmdrec->n_key == mt->un_m_key) {
 526                         /* Printing name of the underlying metadevice */
 527                         (void) printf(" %s", tmpmdrec->n_name);
 528                         underlying_device = NOT_PHYSICAL_DEV;
 529                         break;
 530                 }
 531         }
 532 
 533         /*
 534          * If a metadevice was not found, then the underlying device must be a
 535          * physical device.  Otherwise, call the functions to process the
 536          * underlying devices.
 537          */
 538         if (underlying_device == PHYSICAL_DEV) {
 539                 print_physical_device(phys_nm, mt->un_m_key);
 540                 (void) printf("\n");
 541         } else {
 542                 /* process underlying component */
 543                 (void) printf("\n");
 544                 indent += META_INDENT;
 545                 tmpmdrec->dfunc(mdimpp, indent, phys_nm, tmpmdrec);
 546         }
 547 
 548         /*
 549          * Removing the md_entry from the list
 550          * of all metadevices
 551          */
 552         free_mdrec_list_entry(&mdrec);
 553 }
 554 
 555 
 556 /*
 557  * process_hotspare()
 558  *
 559  * Searches though list of physical devices to match hotspare record.
 560  * Prints physical device name and state of a hotspare unit.
 561  *
 562  */
 563 /*ARGSUSED*/
 564 static void
 565 process_hotspare(
 566         md_im_rec_t     **mdimpp,
 567         int             indent,
 568         pnm_rec_t       *phys_nm,
 569         md_im_rec_t     *mdrec
 570 )
 571 {
 572         hot_spare_t     *hs;
 573         pnm_rec_t       *tmpphys_nm;
 574         char            *state = NULL;
 575 
 576         hs =  (hot_spare_t *)mdrec->record;
 577 
 578         /*
 579          * Loops through physical namespace to find the device that matches
 580          * the hotspare entry.
 581          */
 582         for (tmpphys_nm = phys_nm; tmpphys_nm != NULL;
 583             tmpphys_nm = tmpphys_nm->next) {
 584                 if (tmpphys_nm->n_key ==
 585                     ((hot_spare_t *)hs)->hs_key) {
 586                         /* Printing name of hotspare device */
 587                         (void) printf(" %s", tmpphys_nm->n_name);
 588                         break;
 589                 }
 590         }
 591 
 592         state = meta_get_hs_state(hs->hs_state);
 593         if (state != NULL)
 594                 (void) printf(" (%s)", state);
 595 
 596         /* Not removing entry, because it can be processed more than once. */
 597 }
 598 
 599 
 600 /*
 601  * process_hotspare_pool()
 602  *
 603  * Prints concise unit information for a hotspare pool metadevice and calls a
 604  * function to process each attached hotspare device.
 605  *
 606  */
 607 static void
 608 process_hotspare_pool(
 609         md_im_rec_t     **mdimpp,
 610         int             indent,
 611         pnm_rec_t       *phys_nm,
 612         md_im_rec_t     *mdrec
 613 )
 614 {
 615         hot_spare_pool_ond_t    *hsp;
 616         int                     i;
 617         md_im_rec_t             *tmpmdrec;
 618 
 619         hsp =  (hot_spare_pool_ond_t *)mdrec->record;
 620 
 621         /*
 622          * Printing name, size, and type of metadevice. Setting size field to
 623          * 0, so that output is the as metastat -c.
 624          */
 625         print_concise_entry(indent, mdrec->n_name,
 626             0, 'h');
 627 
 628         /* Looping through list of attached hotspare devices. */
 629         for (i = 0; i < hsp->hsp_nhotspares; i++) {
 630                 /* Looking for the matching record for the hotspare device. */
 631                 for (tmpmdrec = *mdimpp; tmpmdrec != NULL;
 632                     tmpmdrec = tmpmdrec->next) {
 633                         if (tmpmdrec->hs_record_id == hsp->hsp_hotspares[i]) {
 634                                 /* Calling function to print name of hotspare */
 635                                 tmpmdrec->dfunc(mdimpp, indent, phys_nm,
 636                                     tmpmdrec);
 637                         }
 638                 }
 639         }
 640         (void) printf("\n");
 641 
 642         /*
 643          * Removing the md_entry from the list
 644          * of all metadevices
 645          */
 646         free_mdrec_list_entry(&mdrec);
 647 }
 648 
 649 
 650 /*
 651  * process_raid()
 652  *
 653  * Prints concise unit information for a raid metadevice and calls the
 654  * respective functions to process the underlying metadevices.
 655  *
 656  */
 657 static void
 658 process_raid(
 659         md_im_rec_t     **mdimpp,
 660         int             indent,
 661         pnm_rec_t       *phys_nm,
 662         md_im_rec_t     *mdrec
 663 )
 664 {
 665         mr_unit_t       *mr;
 666         mr_column_t     *mc;
 667         mdc_unit_t      uc;
 668         int             i;
 669         md_im_rec_t     *tmpmdrec;
 670         md_im_rec_t     *hstmpmdrec;
 671         md_im_list_t    *ucomp_head = NULL;
 672         md_im_list_t    *ucomp_tail = NULL;
 673         md_im_list_t    *ucomp = NULL;
 674         pnm_rec_t       *tmpphys_nm;
 675         int             underlying_device;
 676 
 677         mr =  (mr_unit_t *)mdrec->record;
 678         uc = mr->c;
 679 
 680         /* Printing name, size, and type of metadevice */
 681         print_concise_entry(indent, mdrec->n_name,
 682             uc.un_total_blocks, 'r');
 683 
 684         /* Loops through raid columns to find underlying metadevices */
 685         for (i = 0, mc = &mr->un_column[0];  i < mr->un_totalcolumncnt;
 686             i++, mc++) {
 687                 char    *state = NULL;
 688                 char    *hsname = NULL;
 689 
 690                 /*
 691                  * Need to assume that underlying device is a physical device,
 692                  * unless we find a matching metadevice record.
 693                  */
 694                 underlying_device = PHYSICAL_DEV;
 695 
 696                 /*
 697                  * Loops through list of metadevices to find record that matches
 698                  * the underlying device.
 699                  */
 700                 for (tmpmdrec = *mdimpp; tmpmdrec != NULL;
 701                     tmpmdrec = tmpmdrec->next) {
 702                         if (tmpmdrec->n_key == mc->un_orig_key) {
 703                                 /* check if hotspare device enabled */
 704                                 if (mc->un_hs_id !=  NULL) {
 705                                         /*
 706                                          * Find matching metadevice record
 707                                          * for the hotspare device.
 708                                          */
 709                                         for (hstmpmdrec = *mdimpp;
 710                                             hstmpmdrec != NULL;
 711                                             hstmpmdrec = hstmpmdrec->next) {
 712                                                 if (hstmpmdrec->hs_record_id ==
 713                                                     mc->un_hs_id) {
 714                                                         /* print name of hs */
 715                                                         hstmpmdrec->dfunc(
 716                                                             mdimpp, indent,
 717                                                             phys_nm,
 718                                                             hstmpmdrec);
 719                                                         break;
 720                                                 }
 721                                         }
 722                                 }
 723                                 /* print name of underlying metadevice */
 724                                 (void) printf(" %s", tmpmdrec->n_name);
 725                                 underlying_device = NOT_PHYSICAL_DEV;
 726                                 ucomp = Zalloc(sizeof (md_im_list_t));
 727                                 ucomp->mdrec = tmpmdrec;
 728                                 ucomponent_append(&ucomp_head, &ucomp_tail,
 729                                     ucomp);
 730                         }
 731                 }
 732 
 733                 if (underlying_device == PHYSICAL_DEV) {
 734                         print_physical_device(phys_nm, mc->un_orig_key);
 735                 }
 736                 state = meta_get_raid_col_state(mc->un_devstate);
 737 
 738                 /*
 739                  * An underlying hotspare must be a physical device.
 740                  * If support is ever added for soft-partitions under
 741                  * hotspare pools, then this code should be updated to
 742                  * include a search for underlying metadevices.
 743                  */
 744                 if (mc->un_hs_id != 0) {
 745                         for (tmpphys_nm = phys_nm; tmpphys_nm != NULL;
 746                             tmpphys_nm = tmpphys_nm->next) {
 747                                 if (tmpphys_nm->n_key == mc->un_hs_key) {
 748                                         hsname = tmpphys_nm->n_name;
 749                                         break;
 750                                 }
 751                         }
 752                 }
 753 
 754                 if (state != NULL) {
 755                         if (hsname != NULL)
 756                                 (void) printf(" (%s-%s)", state,
 757                                     hsname);
 758                         else
 759                                 (void) printf(" (%s)", state);
 760                 } else if (hsname != NULL) {
 761                         (void) printf(gettext(" (spared-%s)"), hsname);
 762                 }
 763         }
 764         (void) printf("\n");
 765 
 766         /* process underlying components */
 767         indent += META_INDENT;
 768         for (ucomp = ucomp_head; ucomp != NULL;
 769             ucomp = ucomp->next) {
 770                 ucomp->mdrec->dfunc(mdimpp, indent, phys_nm,
 771                     ucomp->mdrec);
 772         }
 773         free_md_im_list_entries(&ucomp_head);
 774 
 775         /*
 776          * Removing the md_entry from the list
 777          * of all metadevices
 778          */
 779         free_mdrec_list_entry(&mdrec);
 780 }
 781 
 782 
 783 /*
 784  * process_mirror()
 785  *
 786  * Prints concise unit information for a mirror metadevice and calls the
 787  * respective functions to process the underlying metadevices.
 788  *
 789  */
 790 static void
 791 process_mirror(
 792         md_im_rec_t     **mdimpp,
 793         int             indent,
 794         pnm_rec_t       *phys_nm,
 795         md_im_rec_t     *mdrec
 796 )
 797 {
 798         mm_unit_t       *mm;
 799         mm_submirror_t  *sm;
 800         mdc_unit_t      uc;
 801         int             i;
 802         md_im_rec_t     *tmpmdrec;
 803         md_im_list_t    *ucomp_head = NULL;
 804         md_im_list_t    *ucomp_tail = NULL;
 805         md_im_list_t    *ucomp = NULL;
 806 
 807         mm =  (mm_unit_t *)mdrec->record;
 808         uc = mm->c;
 809 
 810         /* Printing name, size, and type of metadevice */
 811         print_concise_entry(indent, mdrec->n_name,
 812             uc.un_total_blocks, 'm');
 813 
 814         /* Looping through sub-mirrors to find underlying devices */
 815         for (i = 0, sm = &mm->un_sm[0]; i < mm->un_nsm; i++, sm++) {
 816                 char    *state = NULL;
 817 
 818                 for (tmpmdrec = *mdimpp; tmpmdrec != NULL;
 819                     tmpmdrec = tmpmdrec->next) {
 820                         if (tmpmdrec->n_key == sm->sm_key) {
 821                                 (void) printf(" %s", tmpmdrec->n_name);
 822                                 ucomp = Zalloc(sizeof (md_im_list_t));
 823                                 ucomp->mdrec = tmpmdrec;
 824                                 ucomponent_append(&ucomp_head, &ucomp_tail,
 825                                     ucomp);
 826                         }
 827                 }
 828 
 829                 /*
 830                  * It is not possible to have an underlying physical device
 831                  * for a submirror, so there is no need to search the phys_nm
 832                  * list.
 833                  */
 834 
 835                 /* Printing the state for the submirror */
 836                 state = meta_get_sm_state(sm->sm_state);
 837                 if (state != NULL) {
 838                         (void) printf(" (%s)", state);
 839                 }
 840         }
 841         (void) printf("\n");
 842 
 843         /* process underlying components */
 844         indent += META_INDENT;
 845         for (ucomp = ucomp_head; ucomp != NULL;
 846             ucomp = ucomp->next) {
 847                 ucomp->mdrec->dfunc(mdimpp, indent, phys_nm,
 848                     ucomp->mdrec);
 849         }
 850         free_md_im_list_entries(&ucomp_head);
 851 
 852         /*
 853          * Removing the md_entry from the list
 854          * of all metadevices
 855          */
 856         free_mdrec_list_entry(&mdrec);
 857 }
 858 
 859 
 860 /*
 861  * process_stripe()
 862  *
 863  * Prints concise unit information for a stripe metadevice and calls the
 864  * respective functions to process the underlying metadevices.
 865  *
 866  */
 867 static void
 868 process_stripe(
 869         md_im_rec_t     **mdimpp,
 870         int             indent,
 871         pnm_rec_t       *phys_nm,
 872         md_im_rec_t     *mdrec
 873 )
 874 {
 875         ms_unit_t       *ms;
 876         mdc_unit_t      uc;
 877         md_im_rec_t     *tmpmdrec;
 878         md_im_list_t    *ucomp_head = NULL;
 879         md_im_list_t    *ucomp_tail = NULL;
 880         md_im_list_t    *ucomp = NULL;
 881         pnm_rec_t       *tmpphys_nm;
 882         int             underlying_device;
 883         uint_t          row;
 884 
 885         ms =  (ms_unit_t *)mdrec->record;
 886         uc = ms->c;
 887 
 888         /* Printing name, size, and type of metadevice */
 889         print_concise_entry(indent, mdrec->n_name,
 890             uc.un_total_blocks, 's');
 891 
 892         /* Looping through stripe rows */
 893         for (row = 0; (row < ms->un_nrows); ++row) {
 894                 struct ms_row   *mdr = &ms->un_row[row];
 895                 ms_comp_t       *mdcomp = (void *)&((char *)ms)
 896                     [ms->un_ocomp];
 897                 uint_t          comp, c;
 898 
 899                 /*
 900                  * Looping through the components in each row to find the
 901                  * underlying devices.
 902                  */
 903                 for (comp = 0, c = mdr->un_icomp; (comp < mdr->un_ncomp);
 904                     ++comp, ++c) {
 905                         char            *state = NULL;
 906                         char            *hsname = NULL;
 907                         ms_comp_t       *mdc = &mdcomp[c];
 908                         md_m_shared_t   *mdm = &mdc->un_mirror;
 909 
 910                         /*
 911                          * Need to assume that underlying device is a
 912                          * physical device, unless we find a matching
 913                          * metadevice record.
 914                          */
 915                         underlying_device = PHYSICAL_DEV;
 916 
 917                         for (tmpmdrec = *mdimpp; tmpmdrec != NULL;
 918                             tmpmdrec = tmpmdrec->next) {
 919                                 if (tmpmdrec->n_key == mdc->un_key) {
 920                                         (void) printf(" %s", tmpmdrec->n_name);
 921                                         underlying_device = NOT_PHYSICAL_DEV;
 922                                         ucomp = Zalloc(sizeof (md_im_list_t));
 923                                         ucomp->mdrec = tmpmdrec;
 924                                         ucomponent_append(&ucomp_head,
 925                                             &ucomp_tail, ucomp);
 926                                 }
 927                         }
 928                         /* if an underlying metadevice was not found */
 929                         if (underlying_device == PHYSICAL_DEV) {
 930                                 print_physical_device(phys_nm, mdc->un_key);
 931                         }
 932                         state = meta_get_stripe_state(mdm->ms_state);
 933 
 934                         /*
 935                          * An underlying hotspare must be a physical device.
 936                          * If support is ever added for soft-partitions under
 937                          * hotspare pools, then this code should be updated to
 938                          * include a search for underlying metadevices.
 939                          */
 940                         if (mdm->ms_hs_key != 0) {
 941                                 for (tmpphys_nm = phys_nm; tmpphys_nm != NULL;
 942                                     tmpphys_nm = tmpphys_nm->next) {
 943                                         if (tmpphys_nm->n_key ==
 944                                             mdm->ms_hs_key) {
 945                                                 hsname = tmpphys_nm->n_name;
 946                                                 break;
 947                                         }
 948                                 }
 949                         }
 950                         if (state != NULL) {
 951                                 if (hsname != NULL) {
 952                                         (void) printf(" (%s-%s)", state,
 953                                             hsname);
 954                                 } else {
 955                                         (void) printf(" (%s)", state);
 956                                 }
 957                         } else if (hsname != NULL) {
 958                                 (void) printf(gettext(" (spared-%s)"), hsname);
 959                         }
 960                 }
 961         }
 962         (void) printf("\n");
 963 
 964         /* Process underlying metadevices */
 965         indent += META_INDENT;
 966         for (ucomp = ucomp_head; ucomp != NULL;
 967             ucomp = ucomp->next) {
 968                 ucomp->mdrec->dfunc(mdimpp, indent, phys_nm,
 969                     ucomp->mdrec);
 970         }
 971         free_md_im_list_entries(&ucomp_head);
 972 
 973         /*
 974          * Removing the md_entry from the list
 975          * of all metadevices
 976          */
 977         free_mdrec_list_entry(&mdrec);
 978 }
 979 
 980 
 981 /*
 982  * process_softpart()
 983  *
 984  * Prints concise unit information for a softpart metadevice and calls the
 985  * respective functions to process the underlying metadevices.
 986  *
 987  */
 988 static void
 989 process_softpart(
 990         md_im_rec_t     **mdimpp,
 991         int             indent,
 992         pnm_rec_t       *phys_nm,
 993         md_im_rec_t     *mdrec
 994 )
 995 {
 996         mp_unit_t       *mp;
 997         mdc_unit_t      uc;
 998         md_im_rec_t     *tmpmdrec;
 999         int             underlying_device = PHYSICAL_DEV;
1000 
1001         mp =  (mp_unit_t *)mdrec->record;
1002         uc = mp->c;
1003 
1004         /* Printing name, size, and type of metadevice */
1005         print_concise_entry(indent, mdrec->n_name,
1006             uc.un_total_blocks, 'p');
1007 
1008         /*
1009          * Loops through md_im_rec_t **mdimpp list of all metadevices to find
1010          * record that matches the underlying device.
1011          * Softpartitions can only have one underlying device, so once a
1012          * match is found, we are done.
1013          */
1014         for (tmpmdrec = *mdimpp; tmpmdrec != NULL;
1015             tmpmdrec = tmpmdrec->next) {
1016                 if (tmpmdrec->n_key == mp->un_key) {
1017                         /* Printing name of the underlying metadevice */
1018                         (void) printf(" %s", tmpmdrec->n_name);
1019                         underlying_device = NOT_PHYSICAL_DEV;
1020                         break;
1021                 }
1022         }
1023 
1024         /* This is only executed if an underlying metadevice was not found */
1025         if (underlying_device == PHYSICAL_DEV) {
1026                 print_physical_device(phys_nm, mp->un_key);
1027                 (void) printf("\n");
1028         } else {
1029                 /* Process underlying metadevice */
1030                 (void) printf("\n");
1031                 indent += META_INDENT;
1032                 tmpmdrec->dfunc(mdimpp, indent, phys_nm,
1033                     tmpmdrec);
1034         }
1035 
1036         /*
1037          * Removing the md_entry from the list
1038          * of all metadevices
1039          */
1040         free_mdrec_list_entry(&mdrec);
1041 }
1042 
1043 
1044 /*
1045  * process_toplevel_softparts()
1046  *
1047  * Toplevel softpartions need to be grouped so that their underlying devices
1048  * can be printed just once.
1049  */
1050 static void
1051 process_toplevel_softparts(
1052         md_im_rec_t     **mdimpp,
1053         int             indent,
1054         pnm_rec_t       *phys_nm
1055 )
1056 {
1057         mp_unit_t       *mp;
1058         mdc_unit_t      uc;
1059         md_im_rec_t     *mdrec;
1060         md_im_rec_t     *comp_mdrec; /* pntr to underlying component's record */
1061         md_im_rec_t     *tmp_mdrec, *rm_mdrec;
1062         mp_unit_t       *tmp_mp;
1063         int             underlying_device;
1064 
1065         /*
1066          * Loops through md_im_rec_t **mdimpp list of all metadevices to find
1067          * all softpartions that are toplevel softpartitions(softparts w/out
1068          * a parent). Groups output for these entries so that the function to
1069          * process the underlying metadevice is only called once.
1070          */
1071         for (mdrec = *mdimpp; mdrec != NULL; mdrec = mdrec->next) {
1072 
1073                 underlying_device = PHYSICAL_DEV;
1074                 if ((mdrec->md_type == MDDB_F_SOFTPART) &&
1075                     (mdrec->has_parent == 0)) {
1076                         mp =  (mp_unit_t *)mdrec->record;
1077                         uc = mp->c;
1078                         /* Printing name, size, and type of metadevice */
1079                         print_concise_entry(indent, mdrec->n_name,
1080                             uc.un_total_blocks, 'p');
1081                         /*
1082                          * Looking for record that matches underlying
1083                          * component.
1084                          */
1085                         for (comp_mdrec = *mdimpp; comp_mdrec != NULL;
1086                             comp_mdrec = comp_mdrec->next) {
1087                                 if (comp_mdrec->n_key == mp->un_key) {
1088                                         /* Print name of underlying device */
1089                                         (void) printf(" %s",
1090                                             comp_mdrec->n_name);
1091                                         underlying_device = NOT_PHYSICAL_DEV;
1092                                         break;
1093                                 }
1094                         }
1095                         if (underlying_device == PHYSICAL_DEV) {
1096                                 print_physical_device(phys_nm, mp->un_key);
1097                         }
1098                         (void) printf("\n");
1099 
1100                         /*
1101                          * Looking for any other toplevel softpartitions with
1102                          * same underlying device. We know that all other
1103                          * matching metadevices, that share the same underlying
1104                          * metadevice, are also soft-partitions.
1105                          */
1106                         for (tmp_mdrec = mdrec->next; tmp_mdrec != NULL; ) {
1107                                 tmp_mp = (mp_unit_t *)tmp_mdrec->record;
1108                                 if ((tmp_mdrec->has_parent == 0) &&
1109                                     (tmp_mp->un_key == mp->un_key)) {
1110                                         uc = tmp_mp->c;
1111                                         print_concise_entry(indent,
1112                                             tmp_mdrec->n_name,
1113                                             uc.un_total_blocks, 'p');
1114                                         if (underlying_device ==
1115                                             NOT_PHYSICAL_DEV) {
1116                                                 (void) printf(" %s",
1117                                                     comp_mdrec->n_name);
1118                                         } else {
1119                                                 print_physical_device(
1120                                                     phys_nm, tmp_mp->un_key);
1121                                         }
1122                                         (void) printf("\n");
1123                                         /*
1124                                          * Need to advance so that will not lose
1125                                          * position after removing processed
1126                                          * record.
1127                                          */
1128                                         rm_mdrec = tmp_mdrec;
1129                                         tmp_mdrec = tmp_mdrec->next;
1130                                         /*
1131                                          * Removing the md_entry from the list
1132                                          * of all metadevices.
1133                                          */
1134                                         free_mdrec_list_entry(&rm_mdrec);
1135                                 } else {
1136                                         tmp_mdrec = tmp_mdrec->next;
1137                                 }
1138                         }
1139                         /* Process the underlying device */
1140                         if (underlying_device == NOT_PHYSICAL_DEV) {
1141                                 indent += META_INDENT;
1142                                 comp_mdrec->dfunc(mdimpp, indent, phys_nm,
1143                                     comp_mdrec);
1144                         }
1145                 }
1146         }
1147 }
1148 
1149 
1150 /*
1151  * process_toplevel_devices()
1152  *
1153  * Search through list of metadevices for metadevices of md_type that do not
1154  * have a parent.
1155  *
1156  */
1157 static void
1158 process_toplevel_devices(
1159         md_im_rec_t     **mdimpp,
1160         pnm_rec_t       *pnm,
1161         uint_t          md_type
1162 )
1163 {
1164         md_im_rec_t     *mdrec;
1165         md_im_list_t    *mdrec_tl_tail = NULL;
1166         md_im_list_t    *mdrec_tl_head = NULL;
1167         md_im_list_t    *tmp_tl_list = NULL;
1168         int             indent = 0;
1169 
1170         indent += META_INDENT;
1171 
1172         /*
1173          * Need to group soft partitions so that common underlying device
1174          * are only processed once.
1175          */
1176         if (md_type == MDDB_F_SOFTPART) {
1177                 process_toplevel_softparts(mdimpp, indent, pnm);
1178                 return;
1179         }
1180 
1181         /*
1182          * Search the list of metadevices to find all metadevices that match
1183          * the type and don't have a parent.  Put them on a separate list
1184          * that will be processed.
1185          */
1186         for (mdrec = *mdimpp; mdrec != NULL; mdrec = mdrec->next) {
1187                 if ((mdrec->md_type == md_type)&&(mdrec->has_parent == 0)) {
1188                         tmp_tl_list = Zalloc(sizeof (md_im_list_t));
1189                         tmp_tl_list->mdrec = mdrec;
1190                         tmp_tl_list->next = NULL;
1191                         if (mdrec_tl_tail == NULL) {
1192                                 mdrec_tl_tail = tmp_tl_list;
1193                                 mdrec_tl_head = mdrec_tl_tail;
1194                         } else {
1195                                 mdrec_tl_tail->next = tmp_tl_list;
1196                                 mdrec_tl_tail = mdrec_tl_tail->next;
1197                         }
1198                 }
1199 
1200         }
1201 
1202         /*
1203          * Loop through list and process all top-level metadevices of a
1204          * given type.
1205          */
1206         for (tmp_tl_list = mdrec_tl_head; tmp_tl_list != NULL;
1207             tmp_tl_list = tmp_tl_list->next) {
1208                 tmp_tl_list->mdrec->dfunc(mdimpp, indent, pnm,
1209                     tmp_tl_list->mdrec);
1210         }
1211 
1212         free_md_im_list_entries(&mdrec_tl_head);
1213 }
1214 
1215 
1216 /*
1217  * extract_mduser_data()
1218  *
1219  * Converts or copies the (mddb_rb_t->rb_data) metadevice record to a 64bit
1220  * record.
1221  * Sets the dfunc field to point to the appropriate function to process the
1222  * metadevice.
1223  * Sets the parent field for the metadevice.
1224  * Extracts the name from the NM namespace if it is available, otherwise
1225  * generates it from the metadevice's minor number.
1226  *
1227  * Returns:
1228  *      < 0 for failure
1229  *        0 for success
1230  *
1231  */
1232 static int
1233 extract_mduser_data(
1234         mddb_rb_t               *nm,
1235         md_im_rec_t             *mdrec,
1236         void                    *rbp,
1237         int                     is_32bit_record,
1238         md_error_t              *ep
1239 )
1240 {
1241         mdc_unit_t              uc;
1242         hot_spare_t             *hs;
1243         hot_spare_pool_ond_t    *hsp;
1244         size_t                  newreqsize;
1245         mddb_rb_t               *rbp_nm = nm;
1246         struct nm_rec           *nm_record;
1247         struct nm_name          *nmname;
1248         char                    *uname = NULL;
1249 
1250 
1251         /*LINTED*/
1252         nm_record = (struct nm_rec *)((caddr_t)(&rbp_nm->rb_data));
1253 
1254         /*
1255          * Setting the un_self_id or the hs_self_id, in the case of hotspare
1256          * records, for each metadevice entry. Also setting has_parent and
1257          * setting dfunc so that it points to the correct function to process
1258          * the record type.
1259          * If the record was stored ondisk in 32bit format, then it is
1260          * converted to the 64bits equivalent 64bit format and the memory
1261          * for the 32bit pointer is freed.
1262          */
1263         switch (mdrec->md_type) {
1264                 case MDDB_F_SOFTPART:
1265                         if (is_32bit_record) {
1266                                 mp_unit32_od_t  *small_un;
1267                                 mp_unit_t       *big_un;
1268 
1269                                 small_un = (mp_unit32_od_t *)((uintptr_t)rbp +
1270                                     (sizeof (mddb_rb_t) - sizeof (int)));
1271                                 newreqsize = sizeof (mp_unit_t) +
1272                                     ((small_un->un_numexts - 1) *
1273                                     sizeof (struct mp_ext));
1274                                 big_un = (void *)Zalloc(newreqsize);
1275                                 softpart_convert((caddr_t)small_un,
1276                                     (caddr_t)big_un, SMALL_2_BIG);
1277                                 mdrec->record = (void *)big_un;
1278                         } else {
1279                                 mp_unit_t       *big_un;
1280 
1281                                 big_un = (mp_unit_t *)((uintptr_t)rbp +
1282                                     (sizeof (mddb_rb_t) - sizeof (int)));
1283                                 newreqsize = sizeof (mp_unit_t) +
1284                                     ((big_un->un_numexts - 1) *
1285                                     sizeof (struct mp_ext));
1286                                 mdrec->record = (void *)Zalloc(newreqsize);
1287                                 bcopy(big_un, mdrec->record, newreqsize);
1288                         }
1289                         uc = ((mp_unit_t *)mdrec->record)->c;
1290                         mdrec->dfunc = &process_softpart;
1291                         mdrec->un_self_id = uc.un_self_id;
1292                         mdrec->has_parent = MD_HAS_PARENT(
1293                             uc.un_parent);
1294                         break;
1295                 case MDDB_F_STRIPE:
1296                         if (is_32bit_record) {
1297                                 ms_unit32_od_t  *small_un;
1298                                 ms_unit_t       *big_un;
1299 
1300                                 small_un = (ms_unit32_od_t *)((uintptr_t)rbp +
1301                                     (sizeof (mddb_rb_t) - sizeof (int)));
1302                                 newreqsize = get_big_stripe_req_size(
1303                                     small_un, COMPLETE_STRUCTURE);
1304                                     big_un = (void *)Zalloc(newreqsize);
1305                                 stripe_convert((caddr_t)small_un,
1306                                     (caddr_t)big_un, SMALL_2_BIG);
1307                                 mdrec->record = (void *)big_un;
1308                         } else {
1309                                 ms_unit_t       *big_un;
1310 
1311                                 big_un = (ms_unit_t *)((uintptr_t)rbp +
1312                                     (sizeof (mddb_rb_t) - sizeof (int)));
1313                                 newreqsize = get_stripe_req_size(big_un);
1314                                 mdrec->record = (void *)Zalloc(newreqsize);
1315                                 bcopy(big_un, mdrec->record, newreqsize);
1316                         }
1317                         uc = ((ms_unit_t *)mdrec->record)->c;
1318                         mdrec->dfunc = &process_stripe;
1319                         mdrec->un_self_id = uc.un_self_id;
1320                         mdrec->has_parent = MD_HAS_PARENT(
1321                             uc.un_parent);
1322                         break;
1323                 case MDDB_F_MIRROR:
1324                         if (is_32bit_record) {
1325                                 mm_unit32_od_t  *small_un;
1326                                 mm_unit_t       *big_un;
1327 
1328                                 small_un = (mm_unit32_od_t *)((uintptr_t)rbp +
1329                                     (sizeof (mddb_rb_t) - sizeof (int)));
1330                                 newreqsize = sizeof (mm_unit_t);
1331                                 big_un = (void *)Zalloc(newreqsize);
1332                                 mirror_convert((caddr_t)small_un,
1333                                     (caddr_t)big_un, SMALL_2_BIG);
1334                                 mdrec->record = (void *)big_un;
1335                         } else {
1336                                 mm_unit_t       *big_un;
1337 
1338                                 big_un = (mm_unit_t *)((uintptr_t)rbp +
1339                                     (sizeof (mddb_rb_t) - sizeof (int)));
1340                                 newreqsize = sizeof (mm_unit_t);
1341                                 mdrec->record = (void *)Zalloc(newreqsize);
1342                                 bcopy(big_un, mdrec->record, newreqsize);
1343                         }
1344                         uc = ((mm_unit_t *)mdrec->record)->c;
1345                         mdrec->dfunc = &process_mirror;
1346                         mdrec->un_self_id = uc.un_self_id;
1347                         mdrec->has_parent = MD_HAS_PARENT(
1348                             uc.un_parent);
1349                         break;
1350                 case MDDB_F_RAID:
1351                         if (is_32bit_record) {
1352                                 mr_unit32_od_t  *small_un;
1353                                 mr_unit_t       *big_un;
1354                                 uint_t          ncol;
1355 
1356                                 small_un = (mr_unit32_od_t *)((uintptr_t)rbp +
1357                                     (sizeof (mddb_rb_t) - sizeof (int)));
1358                                 ncol = small_un->un_totalcolumncnt;
1359                                 newreqsize = sizeof (mr_unit_t) +
1360                                     ((ncol - 1) * sizeof (mr_column_t));
1361                                 big_un = (void *)Zalloc(newreqsize);
1362                                 raid_convert((caddr_t)small_un,
1363                                     (caddr_t)big_un, SMALL_2_BIG);
1364                                 mdrec->record = (void *)big_un;
1365                         } else {
1366                                 mr_unit_t       *big_un;
1367                                 uint_t          ncol;
1368 
1369                                 big_un = (mr_unit_t *)((uintptr_t)rbp +
1370                                     (sizeof (mddb_rb_t) - sizeof (int)));
1371                                 ncol = big_un->un_totalcolumncnt;
1372                                 newreqsize = sizeof (mr_unit_t) +
1373                                     ((ncol - 1) * sizeof (mr_column_t));
1374                                 mdrec->record = (void *)Zalloc(newreqsize);
1375                                 bcopy(big_un, mdrec->record, newreqsize);
1376                         }
1377                         uc = ((mr_unit_t *)mdrec->record)->c;
1378                         mdrec->dfunc = &process_raid;
1379                         mdrec->un_self_id = uc.un_self_id;
1380                         mdrec->has_parent = MD_HAS_PARENT(
1381                             uc.un_parent);
1382                         break;
1383                 case MDDB_F_TRANS_MASTER:
1384                         if (is_32bit_record) {
1385                                 mt_unit32_od_t  *small_un;
1386                                 mt_unit_t       *big_un;
1387 
1388                                 small_un = (mt_unit32_od_t *)((uintptr_t)rbp +
1389                                     (sizeof (mddb_rb_t) - sizeof (int)));
1390                                 newreqsize = sizeof (mt_unit_t);
1391                                 big_un = (void *)Zalloc(newreqsize);
1392                                 trans_master_convert((caddr_t)small_un,
1393                                     (caddr_t)big_un, SMALL_2_BIG);
1394                                 mdrec->record = (void *)big_un;
1395                         } else {
1396                                 mt_unit_t       *big_un;
1397 
1398                                 big_un = (mt_unit_t *)((uintptr_t)rbp +
1399                                     (sizeof (mddb_rb_t) - sizeof (int)));
1400                                 newreqsize = sizeof (mt_unit_t);
1401                                 mdrec->record = (void *)Zalloc(newreqsize);
1402                                 bcopy(big_un, mdrec->record, newreqsize);
1403                         }
1404                         uc = ((mt_unit_t *)mdrec->record)->c;
1405                         mdrec->dfunc = &process_trans;
1406                         mdrec->un_self_id = uc.un_self_id;
1407                         mdrec->has_parent = MD_HAS_PARENT(
1408                             uc.un_parent);
1409                         break;
1410                 case MDDB_F_HOTSPARE:
1411                         if (is_32bit_record) {
1412                                 hot_spare32_od_t        *small_un;
1413                                 hot_spare_t             *big_un;
1414 
1415                                 small_un = (hot_spare32_od_t *)((uintptr_t)rbp +
1416                                     (sizeof (mddb_rb_t) - sizeof (int)));
1417                                 newreqsize = sizeof (hot_spare_t);
1418                                 big_un = (void *)Zalloc(newreqsize);
1419                                 hs_convert((caddr_t)small_un,
1420                                     (caddr_t)big_un, SMALL_2_BIG);
1421                                 mdrec->record = (void *)big_un;
1422                         } else {
1423                                 hot_spare_t             *big_un;
1424 
1425                                 big_un = (hot_spare_t *)((uintptr_t)rbp +
1426                                     (sizeof (mddb_rb_t) - sizeof (int)));
1427                                 newreqsize = sizeof (hot_spare_t);
1428                                 mdrec->record = (void *)Zalloc(newreqsize);
1429                                 bcopy(big_un, mdrec->record, newreqsize);
1430                         }
1431                         hs = (hot_spare_t *)mdrec->record;
1432                         mdrec->dfunc = &process_hotspare;
1433                         mdrec->un_self_id = NULL;
1434                         mdrec->hs_record_id = hs->hs_record_id;
1435                         mdrec->has_parent = 1;
1436                         break;
1437                 case MDDB_F_HOTSPARE_POOL:
1438                         /*
1439                          * Ondisk and incore records are always same size.
1440                          */
1441                         hsp = (hot_spare_pool_ond_t *)((uintptr_t)rbp +
1442                             (sizeof (mddb_rb_t) - sizeof (int)));
1443                         newreqsize = sizeof (hot_spare_pool_ond_t) +
1444                             (sizeof (mddb_recid_t) * hsp->hsp_nhotspares);
1445                         mdrec->record = (void *)Zalloc(newreqsize);
1446                         bcopy(hsp, mdrec->record, newreqsize);
1447                         hsp = (hot_spare_pool_ond_t *)mdrec->record;
1448                         mdrec->dfunc = &process_hotspare_pool;
1449                         /*
1450                          * If the hsp has descriptive name we'll get
1451                          * the un_self_id
1452                          */
1453                         if (HSP_ID_IS_FN(hsp->hsp_self_id))
1454                                 mdrec->un_self_id = hsp->hsp_self_id;
1455                         else
1456                                 mdrec->un_self_id = NULL;
1457                         mdrec->has_parent = 0;
1458                         break;
1459                 /* All valid cases have been dealt with */
1460                 default:
1461                         (void) mdmddberror(ep, MDE_DB_NODB, 0, 0, 0, NULL);
1462                         return (-1);
1463         }
1464 
1465         /*
1466          * If metadevice record has an entry in the NM namespace
1467          * then it is copied into the mdrec->n_name field.
1468          */
1469         if (mdrec->un_self_id != NULL) {
1470                 for (nmname = &nm_record->r_name[0]; nmname->n_key != 0;
1471                 /*LINTED*/
1472                     nmname = (struct nm_name *)((char *)nmname +
1473                     NAMSIZ(nmname))) {
1474                         /*
1475                          * Extract the metadevice/hsp name if it is
1476                          * in the namespace.
1477                          *
1478                          * If it is a hot spare pool we will find our
1479                          * match by comparing the NM record's n_key
1480                          * with the extracted key from the hsp_self_id
1481                          * Else, match the un_self_id for the record
1482                          * to the n_minor name in the NM record.
1483                          */
1484                             if (mdrec->md_type == MDDB_F_HOTSPARE_POOL) {
1485                                 if (nmname->n_key ==
1486                                     HSP_ID_TO_KEY(hsp->hsp_self_id)) {
1487                                         mdrec->n_key = nmname->n_key;
1488                                         uname = Strdup(nmname->n_name);
1489                                         mdrec->n_name = uname;
1490                                         break;
1491                                 }
1492                             } else {
1493                                 if ((nmname->n_minor) == (uc.un_self_id)) {
1494                                         (*mdrec).n_key = nmname->n_key;
1495                                         uname = Strdup(nmname->n_name);
1496                                         mdrec->n_name = uname;
1497                                         break;
1498                                 }
1499                             }
1500                 }
1501         }
1502 
1503         /*
1504          * If the metadevice name is not in the namespace, then
1505          * then we will generate the name from the minor number
1506          * for the metadevice.  In the case of records for a hotspare
1507          * pool we use hsp_self_id, otherwise we use un_self_id.
1508          */
1509         if (uname == NULL) {
1510                 if (mdrec->md_type == MDDB_F_HOTSPARE_POOL) {
1511                         uname = Malloc(MAXSIZEMDRECNAME);
1512                         (void) sprintf(uname, "hsp%03u",
1513                             HSP_ID(hsp->hsp_self_id));
1514                         mdrec->n_name = uname;
1515                 } else if (mdrec->md_type != MDDB_F_HOTSPARE) {
1516                         /*
1517                          * Generate the metadevice name for all other records
1518                          * (except for hotspares, because hotspares can only
1519                          * be physical devices.)
1520                          */
1521                         uname = Malloc(MAXSIZEMDRECNAME);
1522                         (void) sprintf(uname, "d%lu",
1523                             MD_MIN2UNIT(mdrec->un_self_id));
1524                         mdrec->n_name = uname;
1525                 }
1526         }
1527 
1528         return (0);
1529 }
1530 
1531 
1532 /*
1533  * read_mdrecord()
1534  *
1535  * Reads the mddb_rb32_od_t or mddb_rb_t and the associated metadevice record
1536  * from the disk.  Runs magic, checksum, and revision checks on the record
1537  * block.
1538  *
1539  * Returns:
1540  *      < 0 for failure
1541  *        0 for success
1542  *
1543  */
1544 static int
1545 read_mdrecord(
1546         md_im_rec_t     **mdimpp,
1547         mddb_mb_t       *mbp,
1548         mddb_rb_t       *nm,
1549         mddb_de_t       *dep,
1550         char            *diskname,
1551         int             fd,
1552         md_timeval32_t  *lastaccess,
1553         md_error_t      *ep
1554 )
1555 {
1556         int             cnt, rval = 0;
1557         daddr_t         pblk;
1558         md_im_rec_t     *tmp_mdrec;
1559         void            *rbp = NULL;
1560         char            *rbp_tmp = NULL;
1561         mddb_rb32_t     *rbp_32;
1562         mddb_rb_t       *rbp_64;
1563         crc_skip_t      *skip = NULL;
1564         int             is_32bit_record;
1565 
1566         tmp_mdrec = Zalloc(sizeof (md_im_rec_t));
1567         rbp = (void *)Zalloc(dbtob(dep->de_blkcount));
1568         rbp_tmp = (char *)rbp;
1569 
1570         /* Read in the appropriate record and return configurations */
1571         for (cnt = 0; cnt < dep->de_blkcount; cnt++) {
1572                 if ((pblk = getphysblk(dep->de_blks[cnt], mbp)) < 0) {
1573                         rval = mdmddberror(ep, MDE_DB_BLKRANGE,
1574                             NODEV32, MD_LOCAL_SET,
1575                             dep->de_blks[cnt], diskname);
1576                         return (rval);
1577                 }
1578 
1579                 if (lseek(fd, (off_t)dbtob(pblk), SEEK_SET) < 0) {
1580                         rval = mdsyserror(ep, errno, diskname);
1581                         return (rval);
1582                 }
1583 
1584                 if (read(fd, rbp_tmp, DEV_BSIZE) != DEV_BSIZE) {
1585                         rval = mdsyserror(ep, errno, diskname);
1586                         return (rval);
1587                 }
1588 
1589                 rbp_tmp += DEV_BSIZE;
1590         }
1591         tmp_mdrec->md_type = dep->de_flags;
1592 
1593         /*
1594          * The only place to discover whether or not the record is a
1595          * 32bit or 64bit record is from the record's rb_revision field.
1596          * The mddb_rb_t and mddb_rb32_t structures are identical for the
1597          * following fields:
1598          *      rb_magic, rb_revision, rb_checksum, and rb_checksum_fiddle.
1599          * So we can assume that the record is a 32bit structure when we
1600          * check the record's magic number and revision and when we calculate
1601          * the records checksum.
1602          */
1603         rbp_32 = (mddb_rb32_t *)rbp;
1604 
1605         /*
1606          * Checking the magic number for the record block.
1607          */
1608         if (rbp_32->rb_magic != MDDB_MAGIC_RB) {
1609                 rval = -1;
1610                 (void) mdmddberror(ep, MDE_DB_NODB, 0, 0, 0, NULL);
1611                 goto out;
1612         }
1613 
1614         /*
1615          * Checking the revision for the record block. Must match either
1616          * revision for the current 64bit or 32bit record block.  Also,
1617          * setting the flag for whether or not it is a 32bit record.
1618          */
1619         is_32bit_record = 0;
1620         switch (rbp_32->rb_revision) {
1621         case MDDB_REV_RB:
1622         case MDDB_REV_RBFN:
1623                 is_32bit_record = 1;
1624                 break;
1625         case MDDB_REV_RB64:
1626         case MDDB_REV_RB64FN:
1627                 break;
1628         default:
1629                 rval = -1;
1630                 (void) mdmddberror(ep, MDE_DB_NODB, 0, 0, 0, NULL);
1631                 goto out;
1632         }
1633 
1634         /*
1635          * Calculating the checksum for this record block. Need
1636          * to skip the rb's checksum fiddle.
1637          */
1638         skip = (crc_skip_t *)Malloc(sizeof (crc_skip_t));
1639         skip->skip_next = NULL;
1640         skip->skip_offset = offsetof(mddb_rb_t, rb_checksum_fiddle);
1641         skip->skip_size = 3 * sizeof (uint_t);
1642         if (crcchk(rbp_32, &rbp_32->rb_checksum, dep->de_recsize, skip)) {
1643                 rval = -1;
1644                 (void) mdmddberror(ep, MDE_DB_NODB, 0, 0, 0, NULL);
1645                 goto out;
1646         }
1647 
1648         /* mddb_rb_t and mddb_rb32_t differ before the rb_timestamp field */
1649         if (!is_32bit_record) {
1650                 if ((*lastaccess).tv_sec < rbp_32->rb_timestamp.tv_sec) {
1651                     *lastaccess = rbp_32->rb_timestamp;
1652                 } else if ((*lastaccess).tv_sec ==
1653                     rbp_32->rb_timestamp.tv_sec) {
1654                         if ((*lastaccess).tv_usec <
1655                             rbp_32->rb_timestamp.tv_usec)
1656                                 *lastaccess = rbp_32->rb_timestamp;
1657                 }
1658         } else {
1659                 rbp_64 = (mddb_rb_t *)rbp;
1660                 if ((*lastaccess).tv_sec < rbp_64->rb_timestamp.tv_sec) {
1661                     *lastaccess = rbp_64->rb_timestamp;
1662                 } else if ((*lastaccess).tv_sec ==
1663                     rbp_64->rb_timestamp.tv_sec) {
1664                         if ((*lastaccess).tv_usec <
1665                             rbp_64->rb_timestamp.tv_usec)
1666                                 *lastaccess = rbp_64->rb_timestamp;
1667                 }
1668         }
1669 
1670         /* Populates the fields in md_im_rec_t *tmp_mdrec. */
1671         rval = extract_mduser_data(nm, tmp_mdrec, rbp, is_32bit_record, ep);
1672         if (rval < 0)
1673                 goto out;
1674 
1675         /* Adding record to the head of the list of all metadevices. */
1676         tmp_mdrec->prev = NULL;
1677         if (*mdimpp == NULL) {
1678                 tmp_mdrec->next = NULL;
1679                 *mdimpp = tmp_mdrec;
1680         } else {
1681                 (*mdimpp)->prev = tmp_mdrec;
1682                 tmp_mdrec->next = *mdimpp;
1683                 *mdimpp = tmp_mdrec;
1684         }
1685 
1686 out:
1687         /* Free the skip list */
1688         while (skip) {
1689                 crc_skip_t      *skip_rm = skip;
1690 
1691                 skip = skip->skip_next;
1692                 Free(skip_rm);
1693         }
1694 
1695         if (rbp)
1696                 Free(rbp);
1697 
1698         return (rval);
1699 }
1700 
1701 
1702 /*
1703  * read_all_mdrecords()
1704  *
1705  * Reads the directory block and directory entries.
1706  * Runs magic, checksum, and revision checks on the directory block.
1707  *
1708  * Returns:
1709  *      < 0 for failure
1710  *        0 for success
1711  */
1712 static int
1713 read_all_mdrecords(
1714         md_im_rec_t     **mdimpp,
1715         mddb_mb_t       *mbp,
1716         mddb_lb_t       *lbp,
1717         mddb_rb_t       *nm,
1718         mdname_t        *rsp,
1719         int             fd,
1720         md_timeval32_t  *lastaccess,
1721         md_error_t      *ep
1722 )
1723 {
1724         int             dbblk, rval = 0;
1725         char            db[DEV_BSIZE];
1726         mddb_de_t       *dep;
1727         int             desize;
1728         /*LINTED*/
1729         mddb_db_t       *dbp = (mddb_db_t *)&db;
1730 
1731         /* Read in all directory blocks */
1732         for (dbblk = lbp->lb_dbfirstblk;
1733             dbblk != 0;
1734             dbblk = dbp->db_nextblk) {
1735 
1736                 if ((rval = read_database_block(ep, fd, mbp, dbblk,
1737                     dbp, sizeof (db))) <= 0)
1738                         goto out;
1739 
1740                 /*
1741                  * Set ep with error code for MDE_DB_NODB.  This is the
1742                  * error code used in the kernel when there is a problem
1743                  * with reading records in.  Checks the magic number, the
1744                  * revision, and the checksum for each directory block.
1745                  */
1746                 if (dbp->db_magic != MDDB_MAGIC_DB) {
1747                         rval = -1;
1748                         (void) mdmddberror(ep, MDE_DB_NODB, 0, 0, 0, NULL);
1749                         goto out;
1750                 }
1751 
1752                 if (revchk(MDDB_REV_DB, dbp->db_revision)) {
1753                         rval = -1;
1754                         (void) mdmddberror(ep, MDE_DB_NODB, 0, 0, 0, NULL);
1755                         goto out;
1756                 }
1757 
1758                 if (crcchk(dbp, &dbp->db_checksum, MDDB_BSIZE, NULL)) {
1759                         rval = -1;
1760                         (void) mdmddberror(ep, MDE_DB_NODB, 0, 0, 0, NULL);
1761                         goto out;
1762                 }
1763 
1764                 /*
1765                  * If db timestamp is more recent than the previously recorded
1766                  * last modified timestamp, then update last modified.
1767                  */
1768                 if ((*lastaccess).tv_sec < dbp->db_timestamp.tv_sec) {
1769                         *lastaccess = dbp->db_timestamp;
1770                 } else if ((*lastaccess).tv_sec == dbp->db_timestamp.tv_sec) {
1771                         if ((*lastaccess).tv_usec < dbp->db_timestamp.tv_usec)
1772                                 *lastaccess = dbp->db_timestamp;
1773                 }
1774 
1775                 /* Creates dep list of all directory entries in the db */
1776                 if (dbp->db_firstentry != NULL) {
1777                         /* LINTED */
1778                         dep = (mddb_de_t *)((caddr_t)(&dbp->db_firstentry)
1779                             + sizeof (dbp->db_firstentry));
1780                         dbp->db_firstentry = dep;
1781                         while (dep && dep->de_next) {
1782                                 desize = sizeof (*dep) -
1783                                     sizeof (dep->de_blks) +
1784                                     sizeof (daddr_t) * dep->de_blkcount;
1785                                 /* LINTED */
1786                                 dep->de_next = (mddb_de_t *)
1787                                     ((caddr_t)dep + desize);
1788                                 dep = dep->de_next;
1789                         }
1790                 }
1791 
1792                 /*
1793                  * Process all directory entries in the directory block.
1794                  * For each directory entry, read_mdrec is called to read
1795                  * in the record data.
1796                  */
1797                 for (dep = dbp->db_firstentry; dep != NULL;
1798                     dep = dep->de_next) {
1799 
1800                         /*
1801                          * de_flags is set to the type of metadevice.
1802                          * If directory entry does not correspond to a
1803                          * specific metadevice then it is set to zero.
1804                          * All namespace records(NM, SHR_NM, DID_SHR_NM) have a
1805                          * value of zero in their de_flags field.
1806                          */
1807                         if ((dep->de_flags != 0)&&
1808                             (dep->de_flags != MDDB_F_OPT) &&
1809                             (dep->de_flags != MDDB_F_TRANS_LOG) &&
1810                             (dep->de_flags != MDDB_F_CHANGELOG)) {
1811                                 rval = read_mdrecord(mdimpp, mbp, nm, dep,
1812                                     rsp->cname, fd, lastaccess, ep);
1813                                 if (rval < 0)
1814                                         goto out;
1815                         }
1816                 }
1817         }
1818 
1819 out:
1820         return (rval);
1821 }
1822 
1823 
1824 /*
1825  * report_metastat_info()
1826  *
1827  * Generates the metastat -c output.  Also, updates the global variable
1828  * for a last accessed timestamp.
1829  *
1830  * Returns:
1831  *      < 0 for failure
1832  *        0 for success
1833  *
1834  */
1835 int
1836 report_metastat_info(
1837         mddb_mb_t               *mb,
1838         mddb_lb_t               *lbp,
1839         mddb_rb_t               *nm,
1840         pnm_rec_t               **pnm,
1841         mdname_t                *rsp,
1842         int                     fd,
1843         md_timeval32_t          *lastaccess,
1844         md_error_t              *ep
1845 )
1846 {
1847         int rval = 0;
1848         /* list of all metadevices in diskset */
1849         md_im_rec_t     *mdimp = NULL;
1850         md_im_rec_t     *tmp_mdrec, *rm_mdrec;
1851 
1852         /* Read in metadevice records and add entries to mdimp list. */
1853         rval = read_all_mdrecords(&mdimp, mb, lbp, nm, rsp, fd, lastaccess,
1854             ep);
1855         if (rval < 0)
1856                 goto out;
1857 
1858         /* Adding a fake record to the head of the list of all metadevices. */
1859         if (mdimp != NULL) {
1860                 tmp_mdrec = Zalloc(sizeof (md_im_rec_t));
1861                 tmp_mdrec->prev = NULL;
1862                 mdimp->prev = tmp_mdrec;
1863                 tmp_mdrec->next = mdimp;
1864                 mdimp = tmp_mdrec;
1865         }
1866 
1867         /* Calling functions to process all metadevices on mdimp list */
1868         process_toplevel_devices(&mdimp, *pnm, MDDB_F_SOFTPART);
1869         process_toplevel_devices(&mdimp, *pnm, MDDB_F_TRANS_MASTER);
1870         process_toplevel_devices(&mdimp, *pnm, MDDB_F_MIRROR);
1871         process_toplevel_devices(&mdimp, *pnm, MDDB_F_RAID);
1872         process_toplevel_devices(&mdimp, *pnm, MDDB_F_STRIPE);
1873         process_toplevel_devices(&mdimp, *pnm, MDDB_F_HOTSPARE_POOL);
1874         (void) printf("\n");
1875 
1876 out:
1877         /*
1878          * If mdreclist is not null, then this will walk through all
1879          * elements and free them.
1880          */
1881         tmp_mdrec = mdimp;
1882         while (tmp_mdrec != NULL) {
1883                 rm_mdrec = tmp_mdrec;
1884                 tmp_mdrec = tmp_mdrec->next;
1885                 if (rm_mdrec->record != NULL)
1886                         Free(rm_mdrec->record);
1887                 if (rm_mdrec->n_name != NULL)
1888                         Free(rm_mdrec->n_name);
1889                 Free(rm_mdrec);
1890         }
1891 
1892         free_pnm_rec_list(pnm);
1893         return (rval);
1894 }