diff usr/src/cmd/format/checkdev.c @ 0:c9caec207d52 b86

Initial porting based on b86
author Koji Uno <koji.uno@sun.com>
date Tue, 02 Jun 2009 18:56:50 +0900
parents
children 1a15d5aaf794
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/format/checkdev.c	Tue Jun 02 18:56:50 2009 +0900
@@ -0,0 +1,807 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+#pragma ident	"@(#)checkdev.c	1.18	06/04/27 SMI"
+
+/*
+ * This file contains miscellaneous device validation routines.
+ */
+
+#include "global.h"
+#include <sys/mnttab.h>
+#include <sys/mntent.h>
+#include <sys/autoconf.h>
+
+#include <signal.h>
+#include <malloc.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <sys/ioctl.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#include <sys/swap.h>
+#include <sys/sysmacros.h>
+#include <sys/mkdev.h>
+#include <sys/modctl.h>
+#include <ctype.h>
+#include <libdiskmgt.h>
+#include <libnvpair.h>
+#include "misc.h"
+#include "checkdev.h"
+
+/* Function prototypes */
+#ifdef	__STDC__
+
+static struct 	swaptable *getswapentries(void);
+static void 	freeswapentries(struct swaptable *);
+static int	getpartition(char *pathname);
+static int 	checkpartitions(int bm_mounted);
+
+#else	/* __STDC__ */
+
+static struct swaptable *getswapentries();
+static void freeswapentries();
+static int	getpartition();
+static int 	checkpartitions();
+
+#endif	/* __STDC__ */
+
+extern char	*getfullname();
+
+static struct swaptable *
+getswapentries(void)
+{
+	register struct swaptable *st;
+	register struct swapent *swapent;
+	int	i, num;
+	char	fullpathname[MAXPATHLEN];
+
+	/*
+	 * get the number of swap entries
+	 */
+	if ((num = swapctl(SC_GETNSWP, (void *)NULL)) == -1) {
+		err_print("swapctl error ");
+		fullabort();
+	}
+	if (num == 0)
+		return (NULL);
+	if ((st = (swaptbl_t *)malloc(num * sizeof (swapent_t) + sizeof (int)))
+			== NULL) {
+		err_print("getswapentries: malloc  failed.\n");
+		fullabort();
+	}
+	swapent = st->swt_ent;
+	for (i = 0; i < num; i++, swapent++) {
+		if ((swapent->ste_path = malloc(MAXPATHLEN)) == NULL) {
+			err_print("getswapentries: malloc  failed.\n");
+			fullabort();
+		}
+	}
+	st->swt_n = num;
+	if ((num = swapctl(SC_LIST, (void *)st)) == -1) {
+		err_print("swapctl error ");
+		fullabort();
+	}
+	swapent = st->swt_ent;
+	for (i = 0; i < num; i++, swapent++) {
+		if (*swapent->ste_path != '/') {
+			(void) snprintf(fullpathname, sizeof (fullpathname),
+			    "/dev/%s", swapent->ste_path);
+			(void) strcpy(swapent->ste_path, fullpathname);
+		}
+	}
+	return (st);
+}
+
+static void
+freeswapentries(st)
+struct swaptable *st;
+{
+	register struct swapent *swapent;
+	int i;
+
+	swapent = st->swt_ent;
+	for (i = 0; i < st->swt_n; i++, swapent++)
+		free(swapent->ste_path);
+	free(st);
+
+}
+
+/*
+ *  function getpartition:
+ */
+static int
+getpartition(pathname)
+char *pathname;
+{
+	int		mfd;
+	struct dk_cinfo dkinfo;
+	struct stat	stbuf;
+	char		raw_device[MAXPATHLEN];
+	int		found = -1;
+
+	/*
+	 * Map the block device name to the raw device name.
+	 * If it doesn't appear to be a device name, skip it.
+	 */
+	if (match_substr(pathname, "/dev/") == 0)
+		return (found);
+	(void) strcpy(raw_device, "/dev/r");
+	(void) strcat(raw_device, pathname + strlen("/dev/"));
+	/*
+	 * Determine if this appears to be a disk device.
+	 * First attempt to open the device.  If if fails, skip it.
+	 */
+	if ((mfd = open(raw_device, O_RDWR | O_NDELAY)) < 0) {
+		return (found);
+	}
+	/*
+	 * Must be a character device
+	 */
+	if (fstat(mfd, &stbuf) == -1 || !S_ISCHR(stbuf.st_mode)) {
+		(void) close(mfd);
+		return (found);
+	}
+	/*
+	 * Attempt to read the configuration info on the disk.
+	 */
+	if (ioctl(mfd, DKIOCINFO, &dkinfo) < 0) {
+		(void) close(mfd);
+		return (found);
+	}
+	/*
+	 * Finished with the opened device
+	 */
+	(void) close(mfd);
+
+	/*
+	 * If it's not the disk we're interested in, it doesn't apply.
+	 */
+	if (cur_disk->disk_dkinfo.dki_ctype != dkinfo.dki_ctype ||
+		cur_disk->disk_dkinfo.dki_cnum != dkinfo.dki_cnum ||
+		cur_disk->disk_dkinfo.dki_unit != dkinfo.dki_unit ||
+		strcmp(cur_disk->disk_dkinfo.dki_dname,
+				dkinfo.dki_dname) != 0) {
+		return (found);
+	}
+
+	/*
+	 *  Extract the partition that is mounted.
+	 */
+	return (PARTITION(stbuf.st_rdev));
+}
+
+/*
+ * This Routine checks to see if there are partitions used for swapping overlaps
+ * a given portion of a disk. If the start parameter is < 0, it means
+ * that the entire disk should be checked
+ */
+int
+checkswap(start, end)
+	diskaddr_t start, end;
+{
+	struct swaptable *st;
+	struct swapent *swapent;
+	int		i;
+	int		found = 0;
+	struct dk_map32	*map;
+	int		part;
+
+	/*
+	 * If we are only checking part of the disk, the disk must
+	 * have a partition map to check against.  If it doesn't,
+	 * we hope for the best.
+	 */
+	if (cur_parts == NULL)
+		return (0);
+
+	/*
+	 * check for swap entries
+	 */
+	st = getswapentries();
+	/*
+	 * if there are no swap entries return.
+	 */
+	if (st == (struct swaptable *)NULL)
+		return (0);
+	swapent = st->swt_ent;
+	for (i = 0; i < st->swt_n; i++, swapent++) {
+		if ((part = getpartition(swapent->ste_path)) != -1) {
+			if (start == UINT_MAX64) {
+				found = -1;
+				break;
+			}
+			map = &cur_parts->pinfo_map[part];
+			if ((start >= (int)(map->dkl_cylno * spc() +
+				map->dkl_nblk)) || (end < (int)(map->dkl_cylno
+							* spc()))) {
+					continue;
+			}
+			found = -1;
+			break;
+		};
+	}
+	freeswapentries(st);
+	/*
+	 * If we found trouble and we're running from a command file,
+	 * quit before doing something we really regret.
+	 */
+
+	if (found && option_f) {
+		err_print(
+"Operation on disks being used for swapping must be interactive.\n");
+		cmdabort(SIGINT);
+	}
+
+	return (found);
+
+
+}
+/*
+ * Determines if there are partitions that are a part of an SVM, VxVM, zpool
+ * volume or a live upgrade device,  overlapping a given portion of a disk.
+ * Mounts and swap devices are checked in legacy format code.
+ */
+int
+checkdevinuse(char *cur_disk_path, diskaddr_t start, diskaddr_t end, int print,
+	int check_label)
+{
+
+	int 		error;
+	int 		found = 0;
+	int		check = 0;
+	int 		i;
+	int		bm_inuse = 0;
+	int		part = 0;
+	uint64_t	slice_start, slice_size;
+	dm_descriptor_t	*slices = NULL;
+	nvlist_t	*attrs = NULL;
+	char		*usage;
+	char		*name;
+
+	/*
+	 * If the user does not want to do in use checking, return immediately.
+	 * Normally, this is handled in libdiskmgt. For format, there is more
+	 * processing required, so we want to bypass the in use checking
+	 * here.
+	 */
+
+	if (NOINUSE_SET)
+		return (0);
+
+	/*
+	 * Skip if it is not a real disk
+	 *
+	 * There could be two kinds of strings in cur_disk_path
+	 * One starts with c?t?d?, while the other is a absolute path of a
+	 * block device file.
+	 */
+
+	if (*cur_disk_path != 'c') {
+		struct	stat	stbuf;
+		char		majorname[16];
+		major_t		majornum;
+
+		(void) stat(cur_disk_path, &stbuf);
+		majornum = major(stbuf.st_rdev);
+		(void) modctl(MODGETNAME, majorname, sizeof (majorname),
+				&majornum);
+
+		if (strcmp(majorname, "sd"))
+			if (strcmp(majorname, "ssd"))
+				if (strcmp(majorname, "cmdk"))
+					return (0);
+	}
+
+	/*
+	 * Truncate the characters following "d*", such as "s*" or "p*"
+	 */
+	cur_disk_path = basename(cur_disk_path);
+	name = strrchr(cur_disk_path, 'd');
+	if (name) {
+		name++;
+		for (; (*name <= '9') && (*name >= '0'); name++);
+		*name = (char)0;
+	}
+
+
+	/*
+	 * For format, we get basic 'in use' details from libdiskmgt. After
+	 * that we must do the appropriate checking to see if the 'in use'
+	 * details require a bit of additional work.
+	 */
+
+	dm_get_slices(cur_disk_path, &slices, &error);
+	if (error) {
+		err_print("Error occurred with device in use checking: %s\n",
+		    strerror(error));
+		return (found);
+	}
+	if (slices == NULL)
+		return (found);
+
+	for (i = 0; slices[i] != NULL; i++) {
+		/*
+		 * If we are checking the whole disk
+		 * then any and all in use data is
+		 * relevant.
+		 */
+		if (start == UINT_MAX64) {
+			name = dm_get_name(slices[i], &error);
+			if (error != 0 || !name) {
+				err_print("Error occurred with device "
+				    "in use checking: %s\n",
+				    strerror(error));
+				continue;
+			}
+			if (dm_inuse(name, &usage, DM_WHO_FORMAT, &error) ||
+			    error) {
+				if (error != 0) {
+					dm_free_name(name);
+					name = NULL;
+					err_print("Error occurred with device "
+					    "in use checking: %s\n",
+					    strerror(error));
+					continue;
+				}
+				dm_free_name(name);
+				name = NULL;
+				/*
+				 * If this is a dump device, then it is
+				 * a failure. You cannot format a slice
+				 * that is a dedicated dump device.
+				 */
+
+				if (strstr(usage, DM_USE_DUMP)) {
+					if (print) {
+						err_print(usage);
+						free(usage);
+					}
+					dm_free_descriptors(slices);
+					return (1);
+				}
+				/*
+				 * We really found a device that is in use.
+				 * Set 'found' for the return value, and set
+				 * 'check' to indicate below that we must
+				 * get the partition number to set bm_inuse
+				 * in the event we are trying to label this
+				 * device. check_label is set when we are
+				 * checking modifications for in use slices
+				 * on the device.
+				 */
+				found ++;
+				check = 1;
+				if (print) {
+					err_print(usage);
+					free(usage);
+				}
+			}
+		} else {
+			/*
+			 * Before getting the in use data, verify that the
+			 * current slice is within the range we are checking.
+			 */
+			attrs = dm_get_attributes(slices[i], &error);
+			if (error) {
+				err_print("Error occurred with device in use "
+				    "checking: %s\n", strerror(error));
+				continue;
+			}
+			if (attrs == NULL) {
+				continue;
+			}
+
+			(void) nvlist_lookup_uint64(attrs, DM_START,
+			    &slice_start);
+			(void) nvlist_lookup_uint64(attrs, DM_SIZE,
+			    &slice_size);
+			if (start >= (slice_start + slice_size) ||
+			    (end < slice_start)) {
+				nvlist_free(attrs);
+				attrs = NULL;
+				continue;
+			}
+			name = dm_get_name(slices[i], &error);
+			if (error != 0 || !name) {
+				err_print("Error occurred with device "
+				    "in use checking: %s\n",
+				    strerror(error));
+				nvlist_free(attrs);
+				attrs = NULL;
+				continue;
+			}
+			if (dm_inuse(name, &usage,
+			    DM_WHO_FORMAT, &error) || error) {
+				if (error != 0) {
+					dm_free_name(name);
+					name = NULL;
+					err_print("Error occurred with device "
+					    "in use checking: %s\n",
+					    strerror(error));
+					nvlist_free(attrs);
+					attrs = NULL;
+					continue;
+				}
+				dm_free_name(name);
+				name = NULL;
+				/*
+				 * If this is a dump device, then it is
+				 * a failure. You cannot format a slice
+				 * that is a dedicated dump device.
+				 */
+				if (strstr(usage, DM_USE_DUMP)) {
+					if (print) {
+						err_print(usage);
+						free(usage);
+					}
+					dm_free_descriptors(slices);
+					nvlist_free(attrs);
+					return (1);
+				}
+				/*
+				 * We really found a device that is in use.
+				 * Set 'found' for the return value, and set
+				 * 'check' to indicate below that we must
+				 * get the partition number to set bm_inuse
+				 * in the event we are trying to label this
+				 * device. check_label is set when we are
+				 * checking modifications for in use slices
+				 * on the device.
+				 */
+				found ++;
+				check = 1;
+				if (print) {
+					err_print(usage);
+					free(usage);
+				}
+			}
+		}
+		/*
+		 * If check is set it means we found a slice(the current slice)
+		 * on this device in use in some way.  We potentially want
+		 * to check this slice when labeling is
+		 * requested. We set bm_inuse with this partition value
+		 * for use later if check_label was set when called.
+		 */
+		if (check) {
+			name = dm_get_name(slices[i], &error);
+			if (error != 0 || !name) {
+				err_print("Error occurred with device "
+				    "in use checking: %s\n",
+				    strerror(error));
+				nvlist_free(attrs);
+				attrs = NULL;
+				continue;
+			}
+			part = getpartition(name);
+			dm_free_name(name);
+			name = NULL;
+			if (part != -1) {
+				bm_inuse |= 1 << part;
+			}
+			check = 0;
+		}
+		/*
+		 * If we have attributes then we have successfully
+		 * found the slice we were looking for and we also
+		 * know this means we are not searching the whole
+		 * disk so break out of the loop
+		 * now.
+		 */
+		if (attrs) {
+			nvlist_free(attrs);
+			break;
+		}
+	}
+
+	if (slices) {
+		dm_free_descriptors(slices);
+	}
+
+	/*
+	 * The user is trying to label the disk. We have to do special
+	 * checking here to ensure they are not trying to modify a slice
+	 * that is in use in an incompatible way.
+	 */
+	if (check_label && bm_inuse) {
+		/*
+		 * !0 indicates that we found a
+		 * problem. In this case, we have overloaded
+		 * the use of checkpartitions to work for
+		 * in use devices. bm_inuse is representative
+		 * of the slice that is in use, not that
+		 * is mounted as is in the case of the normal
+		 * use of checkpartitions.
+		 *
+		 * The call to checkpartitions will return !0 if
+		 * we are trying to shrink a device that we have found
+		 * to be in use above.
+		 */
+		return (checkpartitions(bm_inuse));
+	}
+
+	return (found);
+}
+/*
+ * This routine checks to see if there are mounted partitions overlapping
+ * a given portion of a disk.  If the start parameter is < 0, it means
+ * that the entire disk should be checked.
+ */
+int
+checkmount(start, end)
+	diskaddr_t	start, end;
+{
+	FILE		*fp;
+	int		found = 0;
+	struct dk_map32	*map;
+	int		part;
+	struct mnttab	mnt_record;
+	struct mnttab	*mp = &mnt_record;
+
+	/*
+	 * If we are only checking part of the disk, the disk must
+	 * have a partition map to check against.  If it doesn't,
+	 * we hope for the best.
+	 */
+	if (cur_parts == NULL)
+		return (0);
+
+	/*
+	 * Lock out interrupts because of the mntent protocol.
+	 */
+	enter_critical();
+	/*
+	 * Open the mount table.
+	 */
+	fp = fopen(MNTTAB, "r");
+	if (fp == NULL) {
+		err_print("Unable to open mount table.\n");
+		fullabort();
+	}
+	/*
+	 * Loop through the mount table until we run out of entries.
+	 */
+	while ((getmntent(fp, mp)) != -1) {
+
+		if ((part = getpartition(mp->mnt_special)) == -1)
+			continue;
+
+		/*
+		 * It's a mount on the disk we're checking.  If we are
+		 * checking whole disk, then we found trouble.  We can
+		 * quit searching.
+		 */
+		if (start == UINT_MAX64) {
+			found = -1;
+			break;
+		}
+
+		/*
+		 * If the partition overlaps the zone we're checking,
+		 * then we found trouble.  We can quit searching.
+		 */
+		map = &cur_parts->pinfo_map[part];
+		if ((start >= (int)(map->dkl_cylno * spc() + map->dkl_nblk)) ||
+			(end < (int)(map->dkl_cylno * spc()))) {
+			continue;
+		}
+		found = -1;
+		break;
+	}
+	/*
+	 * Close down the mount table.
+	 */
+	(void) fclose(fp);
+	exit_critical();
+
+	/*
+	 * If we found trouble and we're running from a command file,
+	 * quit before doing something we really regret.
+	 */
+
+	if (found && option_f) {
+		err_print("Operation on mounted disks must be interactive.\n");
+		cmdabort(SIGINT);
+	}
+	/*
+	 * Return the result.
+	 */
+	return (found);
+}
+
+int
+check_label_with_swap()
+{
+	int			i;
+	struct swaptable *st;
+	struct swapent *swapent;
+	int	part;
+	int	bm_swap = 0;
+
+	/*
+	 * If we are only checking part of the disk, the disk must
+	 * have a partition map to check against.  If it doesn't,
+	 * we hope for the best.
+	 */
+	if (cur_parts == NULL)
+		return (0);	/* Will be checked later */
+
+	/*
+	 * Check for swap entries
+	 */
+	st = getswapentries();
+	/*
+	 * if there are no swap entries return.
+	 */
+	if (st == (struct swaptable *)NULL)
+		return (0);
+	swapent = st->swt_ent;
+	for (i = 0; i < st->swt_n; i++, swapent++)
+		if ((part = getpartition(swapent->ste_path)) != -1)
+				bm_swap |= (1 << part);
+	freeswapentries(st);
+
+	return (checkpartitions(bm_swap));
+}
+
+/*
+ * Check the new label with the existing label on the disk,
+ * to make sure that any mounted partitions are not being
+ * affected by writing the new label.
+ */
+int
+check_label_with_mount()
+{
+	FILE			*fp;
+	int			part;
+	struct mnttab		mnt_record;
+	struct mnttab		*mp = &mnt_record;
+	int			bm_mounted = 0;
+
+
+	/*
+	 * If we are only checking part of the disk, the disk must
+	 * have a partition map to check against.  If it doesn't,
+	 * we hope for the best.
+	 */
+	if (cur_parts == NULL)
+		return (0);	/* Will be checked later */
+
+	/*
+	 * Lock out interrupts because of the mntent protocol.
+	 */
+	enter_critical();
+	/*
+	 * Open the mount table.
+	 */
+	fp = fopen(MNTTAB, "r");
+	if (fp == NULL) {
+		err_print("Unable to open mount table.\n");
+		fullabort();
+	}
+	/*
+	 * Loop through the mount table until we run out of entries.
+	 */
+	while ((getmntent(fp, mp)) != -1) {
+		if ((part = getpartition(mp->mnt_special)) != -1)
+			bm_mounted |= (1 << part);
+	}
+	/*
+	 * Close down the mount table.
+	 */
+	(void) fclose(fp);
+	exit_critical();
+
+	return (checkpartitions(bm_mounted));
+
+}
+
+/*
+ * This Routine checks if any partitions specified
+ * are affected by writing the new label
+ */
+static int
+checkpartitions(int bm_mounted)
+{
+	struct dk_map32		*n;
+	struct dk_map		*o;
+	struct dk_allmap	old_map;
+	int			i, found = 0;
+
+	/*
+	 * Now we need to check that the current partition list and the
+	 * previous partition list (which there must be if we actually
+	 * have partitions mounted) overlap  in any way on the mounted
+	 * partitions
+	 */
+
+	/*
+	 * Get the "real" (on-disk) version of the partition table
+	 */
+	if (ioctl(cur_file, DKIOCGAPART, &old_map) == -1) {
+		err_print("Unable to get current partition map.\n");
+		return (-1);
+	}
+	for (i = 0; i < NDKMAP; i++) {
+		if (bm_mounted & (1 << i)) {
+			/*
+			 * This partition is mounted
+			 */
+			o = &old_map.dka_map[i];
+			n = &cur_parts->pinfo_map[i];
+#ifdef DEBUG
+			fmt_print(
+"checkpartitions :checking partition '%c' \n", i + PARTITION_BASE);
+#endif
+			/*
+			 * If partition is identical, we're fine.
+			 * If the partition grows, we're also fine, because
+			 * the routines in partition.c check for overflow.
+			 * It will (ultimately) be up to the routines in
+			 * partition.c to warn about creation of overlapping
+			 * partitions
+			 */
+			if (o->dkl_cylno == n->dkl_cylno &&
+					o->dkl_nblk <= n->dkl_nblk) {
+#ifdef	DEBUG
+				if (o->dkl_nblk < n->dkl_nblk) {
+					fmt_print(
+"- new partition larger by %d blocks", n->dkl_nblk-o->dkl_nblk);
+				}
+				fmt_print("\n");
+#endif
+				continue;
+			}
+#ifdef DEBUG
+			fmt_print("- changes; old (%d,%d)->new (%d,%d)\n",
+				o->dkl_cylno, o->dkl_nblk, n->dkl_cylno,
+				n->dkl_nblk);
+#endif
+			found = -1;
+		}
+		if (found)
+			break;
+	}
+
+	/*
+	 * If we found trouble and we're running from a command file,
+	 * quit before doing something we really regret.
+	 */
+
+	if (found && option_f) {
+		err_print("Operation on mounted disks or \
+disks currently being used for swapping must be interactive.\n");
+		cmdabort(SIGINT);
+	}
+	/*
+	 * Return the result.
+	 */
+	return (found);
+}