/* * check to see if a device is in its set */ int meta_check_inset( mdsetname_t *sp, mdname_t *np, md_error_t *ep ) { mdsetname_t *npsp; int bypass_daemon = FALSE; /* check devices set */ if (metaislocalset(sp)) bypass_daemon = TRUE; if ((npsp = metagetset(np, bypass_daemon, ep)) == NULL) { if ((! metaismeta(np)) && (metaislocalset(sp)) && (mdismddberror(ep, MDE_DB_NODB))) { mdclrerror(ep); npsp = sp; } else { return (-1); } } /* check set */ if (metaissameset(sp, npsp)) return (0); /* return appropriate error */ if (metaislocalset(sp)) return (mddeverror(ep, MDE_IN_SHARED_SET, np->dev, np->cname)); else return (mddeverror(ep, MDE_NOT_IN_SET, np->dev, np->cname)); }
/* * check to see if a device is in a metadevice */ int meta_check_inmeta( mdsetname_t *sp, mdname_t *np, mdchkopts_t options, diskaddr_t slblk, diskaddr_t nblks, md_error_t *ep ) { uint_t partno; /* see if replica slice is ok, only applies to disks in sets */ if (! (options & MDCHK_ALLOW_REPSLICE) && ! metaislocalset(sp)) { uint_t rep_slice; if (metagetvtoc(np, FALSE, &partno, ep) == NULL) return (-1); if (meta_replicaslice(np->drivenamep, &rep_slice, ep) != 0) return (-1); if (partno == rep_slice) return (mddeverror(ep, MDE_REPCOMP_INVAL, np->dev, np->cname)); } /* check for databases */ if (meta_check_inreplica(sp, np, slblk, nblks, ep) != 0) { if (mdisuseerror(ep, MDE_ALREADY)) { if (options & MDCHK_ALLOW_MDDB) { mdclrerror(ep); } else { return (mddeverror(ep, MDE_HAS_MDDB, np->dev, np->cname)); } } else { return (-1); } } /* check metadevices */ if (meta_check_instripe(sp, np, slblk, nblks, ep) != 0) return (-1); if (meta_check_inmirror(sp, np, slblk, nblks, ep) != 0) return (-1); if (meta_check_intrans(sp, np, options, slblk, nblks, ep) != 0) return (-1); if (meta_check_insp(sp, np, slblk, nblks, ep) != 0) return (-1); if (! (options & MDCHK_ALLOW_HS)) { if (meta_check_inhsp(sp, np, slblk, nblks, ep) != 0) return (-1); } if (meta_check_inraid(sp, np, slblk, nblks, ep) != 0) return (-1); /* return success */ return (0); }
/* * FUNCTION: meta_repartition_drive() * INPUT: sp - the set name for the device to check * dnp - the name of the drive to partition * options - options (see NOTES) * OUTPUT: vtocp - pointer to an mdvtoc_t structure in which * to return the new VTOC to the caller * ep - pointer to an md_error_t structure in which * to return errors to the caller * RETURNS: int - 0 - drive was or can be repartitioned * -1 - drive could not or should not be * repartitioned * PURPOSE: Repartition a disk for use in a disk set or in order * to create soft partitions on it. Alternatively, * return the VTOC that the disk would have if it were * repartitioned without actually repartitioning it. * * NOTES: * * This routine will repartition a drive to make it suitable for * inclusion in a diskset. Specifically, it will create a * proposed VTOC that specifies a replica slice that begins at the * first valid lba, is large enough to hold a label and a metadb * replica, does not overlap any other slices, and is unmountable. * If the current replica slice already satisfies those criteria, * the routine will neither create a proposed VTOC nor repartition * the drive unless the MD_REPART_FORCE flag is passed into the * routine in the options argument. If the routine does create a * proposed VTOC, it will return the proposed VTOC in *vtocp if * vtocp isn't NULL. * * The slice to be used as the replica slice is determined by the * function meta_replicaslice(). * * If the replica slice does not satisfy the above criteria or the * MD_REPART_FORCE flag is set, the proposed VTOC will specify a * replica slice that satisfies the above criteria, a slice zero * that contains the remaining space on the disk, and no other * slices. If that repartitioning would cause the replica slice * to move or shrink, and the MD_REPART_LEAVE_REP option is set, * the routine will return -1 without creating or returning a * proposed vtoc, and without repartitioning the disk. Otherwise * the routine will repartition the disk unless the * MD_REPART_DONT_LABEL flag is set in the options argument. * * If the MD_REPART_DONT_LABEL flag is set in the options argument, * but the routine would otherwise repartition the drive, the * routine won't repartition the drive, but will create a proposed * VTOC that satisfies the criteria defined above and return it * it in *vtocp if vtocp isn't NULL, The MD_REPART_DONT_LABEL * option allows calling routines to determine what the contents of * the drive's VTOC would be if the drive were repartitioned without * actually repartitioning the drive. */ int meta_repartition_drive( mdsetname_t *sp, mddrivename_t *dnp, int options, mdvtoc_t *vtocp, md_error_t *ep ) { uint_t replicaslice; diskaddr_t first_lba, last_lba; int round_sizes = 1; unsigned long long cylsize; unsigned long long drvsize; int i; mdgeom_t *mdgp; mdvtoc_t *mdvp; mdvtoc_t proposed_vtoc; uint_t reservedcyl; ushort_t resflag; mdname_t *resnp; unsigned long long ressize; md_set_desc *sd; daddr_t dbsize; diskaddr_t replica_start; diskaddr_t replica_size; diskaddr_t replica_end; diskaddr_t data_start; diskaddr_t data_size; if (meta_replicaslice(dnp, &replicaslice, ep) != 0) { return (-1); } /* Don't round for EFI disks */ if (replicaslice == MD_SLICE6) round_sizes = 0; /* * We took as argument a drive name pointer, but we need a * slice name pointer to retrieve vtoc information. So get * the name pointer for slice zero first, then use it to get * the vtoc info for the disk. */ if ((resnp = metaslicename(dnp, MD_SLICE0, ep)) == NULL) return (-1); if ((mdvp = metagetvtoc(resnp, FALSE, NULL, ep)) == NULL) return (-1); /* * Determine the metadb size. */ dbsize = MD_DBSIZE; if (!metaislocalset(sp)) { if ((sd = metaget_setdesc(sp, ep)) == NULL) return (-1); if (MD_MNSET_DESC(sd)) dbsize = MD_MN_DBSIZE; } /* If we've got an efi disk, we better have lba info */ first_lba = mdvp->first_lba; last_lba = mdvp->last_lba; ASSERT((round_sizes != 0) || (last_lba > 0)); /* * At this point, ressize is used as a minimum value. Later * it will be rounded up to a cylinder boundary if * appropriate. ressize is in units of disk sectors. */ ressize = dbsize + VTOC_SIZE; resflag = V_UNMNT; /* * If we're forcing the repartition, we can skip the replica * slice and overlap tests. */ if (options & MD_REPART_FORCE) { goto do_repartition; } /* * Replica slice tests: it must begin at first_lba, be long * enough, have the right flags, and not overlap any other * slices. If any of these conditions is violated, we need to * repartition the disk. */ if (mdvp->parts[replicaslice].start != first_lba) { goto do_repartition; } if (mdvp->parts[replicaslice].size < ressize) { goto do_repartition; } if (mdvp->parts[replicaslice].flag != resflag) { goto do_repartition; } /* * Check for overlap: this test should use the actual size of * the replica slice, as contained in the vtoc, and NOT the * minimum size calculated above. */ replica_end = first_lba + mdvp->parts[replicaslice].size; for (i = 0; i < mdvp->nparts; i++) { if (i != replicaslice) { if ((mdvp->parts[i].size > 0) && (mdvp->parts[i].start < replica_end)) { goto do_repartition; } } } /* * If we passed the above tests, then the disk is already * partitioned appropriately, and we're not being told to * force a change. */ return (0); do_repartition: /* Retrieve disk geometry info and round to cylinder sizes */ if (round_sizes != 0) { if ((mdgp = metagetgeom(resnp, ep)) == NULL) return (-1); /* * Both cylsize and drvsize are in units of disk * sectors. * * The intended results are of type unsigned long * long. Since each operand of the first * multiplication is of type unsigned int, we risk * overflow by multiplying and then converting the * result. Therefore we explicitly cast (at least) * one of the operands, forcing conversion BEFORE * multiplication, and avoiding overflow. The second * assignment is OK, since one of the operands is * already of the desired type. */ cylsize = ((unsigned long long)mdgp->nhead) * mdgp->nsect; drvsize = cylsize * mdgp->ncyl; /* * How many cylinders must we reserve for the replica * slice to ensure that it meets the previously * calculated minimum size? */ reservedcyl = (ressize + cylsize - 1) / cylsize; ressize = reservedcyl * cylsize; } else { drvsize = last_lba - first_lba; } /* Would this require a forbidden change? */ if (options & MD_REPART_LEAVE_REP) { if ((mdvp->parts[replicaslice].start != first_lba) || (mdvp->parts[replicaslice].size < ressize)) { return (mddeverror(ep, MDE_REPART_REPLICA, resnp->dev, NULL)); } } /* * It seems unlikely that someone would pass us too small a * disk, but it's still worth checking for... */ if (((round_sizes != 0) && (reservedcyl >= (int)mdgp->ncyl)) || ((round_sizes == 0) && (ressize + first_lba >= last_lba))) { return (mdmddberror(ep, MDE_DB_TOOSMALL, meta_getminor(resnp->dev), sp->setno, 0, NULL)); } replica_start = first_lba; replica_size = ressize; data_start = first_lba + ressize; data_size = drvsize - ressize; /* * Create the proposed VTOC. First copy the current VTOC * into the proposed VTOC to duplicate the values that don't * need to change. Then change the partition table and set * the flag value for the replica slice to resflag to reserve it * for metadata. */ proposed_vtoc = *mdvp; /* We need at least replicaslice partitions in the proposed vtoc */ if (replicaslice >= proposed_vtoc.nparts) { proposed_vtoc.nparts = replicaslice + 1; } for (i = 0; i < proposed_vtoc.nparts; i++) { /* don't change the reserved partition of an EFI device */ if (proposed_vtoc.parts[i].tag == V_RESERVED) data_size = proposed_vtoc.parts[i].start - data_start; else (void) memset(&proposed_vtoc.parts[i], '\0', sizeof (proposed_vtoc.parts[i])); } proposed_vtoc.parts[MD_SLICE0].start = data_start; proposed_vtoc.parts[MD_SLICE0].size = data_size; proposed_vtoc.parts[MD_SLICE0].tag = V_USR; proposed_vtoc.parts[replicaslice].start = replica_start; proposed_vtoc.parts[replicaslice].size = replica_size; proposed_vtoc.parts[replicaslice].flag = resflag; proposed_vtoc.parts[replicaslice].tag = V_USR; if (!(options & MD_REPART_DONT_LABEL)) { /* * Label the disk with the proposed VTOC. */ *mdvp = proposed_vtoc; if (metasetvtoc(resnp, ep) != 0) { return (-1); } } if (vtocp != NULL) { /* * Return the proposed VTOC. */ *vtocp = proposed_vtoc; } return (0); }
int main(int argc, char **argv) { char c; char *sname = MD_LOCAL_NAME; mddevopts_t options = 0; md_error_t status = mdnullerror; md_error_t *ep = &status; mdsetname_t *sp = NULL; mdsetname_t *local_sp = NULL; char *argname; int todo = 0; int ret = 0; int md_upgd_stat = 0; int error; md_set_desc *sd; /* * Get the locale set up before calling any other routines * with messages to ouput. Just in case we're not in a build * environment, make sure that TEXT_DOMAIN gets set to * something. */ #if !defined(TEXT_DOMAIN) #define TEXT_DOMAIN "SYS_TEST" #endif (void) setlocale(LC_ALL, ""); (void) textdomain(TEXT_DOMAIN); if ((sdssc_bind_library() == SDSSC_OKAY) && (sdssc_cmd_proxy(argc, argv, SDSSC_PROXY_PRIMARY, &error) == SDSSC_PROXY_DONE)) exit(error); openlog("metadevadm", LOG_ODELAY, LOG_USER); /* initialize */ if (md_init(argc, argv, 0, 1, ep) != 0 || meta_check_root(ep) != 0) { closelog(); mde_perror(ep, ""); md_exit(sp, 1); } /* parse args */ optind = 1; opterr = 1; while ((c = getopt(argc, argv, "vlhnrs:u:")) != -1) { switch (c) { case 'v': options |= DEV_VERBOSE; break; case 'n': options |= DEV_NOACTION; break; case 'r': options |= DEV_RELOAD; todo = 1; break; case 's': sname = optarg; break; case 'u': todo = 1; options |= DEV_UPDATE; argname = optarg; if (argname == NULL) { usage("metadevadm"); closelog(); md_exit(sp, 0); } break; case 'l': options |= DEV_LOG; break; case 'h': default: usage("metadevadm"); closelog(); md_exit(sp, 0); } } if ((sp = metasetname(sname, ep)) == NULL) { mde_perror(ep, ""); closelog(); md_exit(sp, 1); } if (!metaislocalset(sp)) { if ((sd = metaget_setdesc(sp, ep)) == NULL) { mde_perror(ep, ""); closelog(); md_exit(sp, 1); } if (MD_MNSET_DESC(sd)) { (void) printf("%s\n", gettext("metadevadm cannot be " "run on multi-owner disksets\n")); closelog(); md_exit(sp, 0); } } if ((options & DEV_VERBOSE) && (todo != 1)) { usage("metadevadm"); closelog(); md_exit(sp, 0); } if ((options & DEV_NOACTION) && (todo != 1)) { usage("metadevadm"); closelog(); md_exit(sp, 0); } if (todo == 0) { usage("metadevadm"); closelog(); md_exit(sp, 0); } if ((local_sp = metasetname(MD_LOCAL_NAME, ep)) == NULL) { mde_perror(ep, ""); closelog(); md_exit(local_sp, 1); } /* lock the local set */ if (meta_lock(local_sp, TRUE, ep) != 0) { mde_perror(ep, ""); closelog(); md_exit(local_sp, 1); } /* grab set lock */ if (meta_lock(sp, TRUE, ep)) { mde_perror(ep, ""); closelog(); md_exit(local_sp, 1); } /* check for ownership */ if (meta_check_ownership(sp, ep) != 0) { /* * If the set is not owned by this node then only update the * local set's replica. */ options |= DEV_LOCAL_SET; } /* * check for upgrade. If upgrade in progress then just exit. */ if (metaioctl(MD_UPGRADE_STAT, &md_upgd_stat, ep, NULL) != 0) { mde_perror(ep, ""); closelog(); (void) meta_unlock(sp, ep); md_exit(local_sp, 1); } if (md_upgd_stat == 0) { ret = meta_fixdevid(sp, options, argname, ep); if (ret == METADEVADM_ERR) { /* * If the call failed, for a DEV_RELOAD still need to * update the .conf file to provide the latest devid * information so exit later. */ if (options & DEV_UPDATE) { closelog(); (void) meta_unlock(sp, ep); md_exit(local_sp, 1); } } } /* * Sync replica list in kernel to replica list in conf files. * This will update driver name and minor number in conf file * if reload was run. Will update device id in conf file if * update was run. */ meta_sync_db_locations(sp, ep); closelog(); (void) meta_unlock(sp, ep); md_exit(local_sp, ret); return (0); }
/* * save metadevice configuration in md.cf */ int meta_update_md_cf( mdsetname_t *sp, md_error_t *ep ) { char *name = METACONF; char *tname = METACONFTMP; FILE *tfp = NULL; FILE *mfp = NULL; mdprtopts_t options = PRINT_SHORT | PRINT_FAST; struct stat sbuf; char line[1000]; /* If this is not the local set, no need to do anything */ if (!metaislocalset(sp)) return (0); /* open temp file */ if ((tfp = fopen(tname, "w")) == NULL) return (mdsyserror(ep, errno, tname)); if (stat(name, &sbuf) == 0) { (void) fchmod(fileno(tfp), (sbuf.st_mode & 0777)); (void) fchown(fileno(tfp), sbuf.st_uid, sbuf.st_gid); } /* dump header */ if (fputs(dgettext(TEXT_DOMAIN, "# metadevice configuration file\n" "# do not hand edit\n"), tfp) == EOF) { (void) mdsyserror(ep, errno, tname); goto errout; } /* dump device configuration */ if (meta_print_all(sp, tname, NULL, tfp, options, NULL, ep) != 0) goto errout; /* close and rename file */ if (fclose(tfp) != 0) { (void) mdsyserror(ep, errno, tname); goto errout; } tfp = NULL; /* * Renames don't work in the miniroot since tmpfiles are * created in /var/tmp. Hence we copy the data out. */ if (rename(tname, name) != 0) { if (errno == EROFS) { if ((tfp = fopen(tname, "r")) == NULL) { goto errout; } if ((mfp = fopen(METACONF, "w+")) == NULL) { goto errout; } while (fgets(line, 1000, tfp) != NULL) { if (fputs(line, mfp) == NULL) { (void) mdsyserror(ep, errno, METACONF); goto errout; } } if (fclose(tfp) != 0) { tfp = NULL; goto errout; } tfp = NULL; /* delete the tempfile */ (void) unlink(tname); if (fflush(mfp) != 0) { goto errout; } if (fsync(fileno(mfp)) != 0) { goto errout; } if (fclose(mfp) != 0) { mfp = NULL; goto errout; } mfp = NULL; } else { (void) mdsyserror(ep, errno, name); goto errout; } } /* success */ return (0); /* cleanup, return error */ errout: if (tfp != NULL) { (void) fclose(tfp); (void) unlink(tname); } if (mfp != NULL) { (void) fclose(mfp); } return (-1); }