Example #1
0
/*
 * 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));
}
Example #2
0
/*
 * 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);
}
Example #3
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);
}
Example #4
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);
}
Example #5
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);
}