OpenSolaris_b135/lib/lvm/libmeta/common/meta_systemfile.c

Compare this file to the similar file:
Show the results in this format:

/*
 * 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	"%Z%%M%	%I%	%E% SMI"

/*
 * 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

/*
 * patch /kernel/drv/md.conf file
 */
#include <sys/types.h>
#include <sys/stat.h>
#include <meta.h>
#include <sys/lvm/md_mddb.h>

/*
 * magic strings in system
 */
#define	BEGROOTSTR	"* Begin MDD root info (do not edit)\n"
#define	ENDROOTSTR	"* End MDD root info (do not edit)\n"
#define	BEGMDDBSTR	"# Begin MDD database info (do not edit)\n"
#define	ENDMDDBSTR	"# End MDD database info (do not edit)\n"

/*
 * copy system file, yank root and database lines
 */
int
meta_systemfile_copy(
	char		*sname,		/* system file name */
	int		doroot,		/* remove mdd root stuff */
	int		domddb,		/* remove mdd database stuff */
	int		doit,		/* really copy file */
	int		verbose,	/* show what we're doing */
	char		**tname,	/* returned temp file name */
	FILE		**tfp,		/* returned open FILE */
	md_error_t	*ep		/* returned error */
)
{
	FILE		*fp;
	struct stat	sbuf;
	char		buf[MDDB_BOOTLIST_MAX_LEN];
	int		delroot = 0;
	int		delmddb = 0;

	/* check names */
	assert(sname != NULL);
	assert(tname != NULL);
	assert(tfp != NULL);

	/* get temp name */
	*tfp = NULL;
	*tname = Malloc(strlen(sname) + strlen(".tmp") + 1);
	(void) strcpy(*tname, sname);
	(void) strcat(*tname, ".tmp");

	/* copy system file, yank stuff */
	if (((fp = fopen(sname, "r")) == NULL) ||
	    (fstat(fileno(fp), &sbuf) != 0)) {
		if (errno != ENOENT) {
			(void) mdsyserror(ep, errno, sname);
			goto out;
		}
	}
	if (doit) {
		if ((*tfp = fopen(*tname, "w")) == NULL) {
			/*
			 * If we are on the miniroot we need to create
			 * files in /var/tmp. Opening a writable file
			 * in the miniroot result is EROFS error.
			 */
			if (errno != EROFS) {
				(void) mdsyserror(ep, errno, *tname);
				goto out;
			}
			Free(*tname);
			*tname = tempnam("/var/tmp", "svm_");
			if (*tname == NULL) {
				(void) mdsyserror(ep, errno, NULL);
				goto out;
			}
			if ((*tfp = fopen(*tname, "w")) == NULL) {
				(void) mdsyserror(ep, errno, *tname);
				goto out;
			}
		}
		if (fp != NULL) {
			if ((fchmod(fileno(*tfp), (sbuf.st_mode & 0777))
			    != 0) ||
			    (fchown(fileno(*tfp), sbuf.st_uid, sbuf.st_gid)
			    != 0)) {
				(void) mdsyserror(ep, errno, *tname);
				goto out;
			}
		}
	}
	if (verbose) {
		(void) printf(dgettext(TEXT_DOMAIN,
		    "Delete the following lines from %s:\n\n"), sname);
	}
	while ((fp != NULL) && (fgets(buf, sizeof (buf), fp) != NULL)) {
		if ((doroot) && (strcmp(buf, BEGROOTSTR) == 0)) {
			delroot = 1;
			if (verbose)
				(void) printf("%s", buf);
			continue;
		}
		if (delroot) {
			if (strcmp(buf, ENDROOTSTR) == 0)
				delroot = 0;
			if (verbose)
				(void) printf("%s", buf);
			continue;
		}
		if ((domddb) && (strcmp(buf, BEGMDDBSTR) == 0)) {
			delmddb = 1;
			if (verbose)
				(void) printf("%s", buf);
			continue;
		}
		if (delmddb) {
			if (strcmp(buf, ENDMDDBSTR) == 0)
				delmddb = 0;
			if (verbose)
				(void) printf("%s", buf);
			continue;
		}
		if (doit) {
			if (fputs(buf, *tfp) == EOF) {
				(void) mdsyserror(ep, errno, *tname);
				goto out;
			}
		}
	}
	if (fp != NULL) {
		if ((! feof(fp)) ||
		    (fclose(fp) != 0)) {
			(void) mdsyserror(ep, errno, sname);
			goto out;
		}
		fp = NULL;
	}
	if (verbose)
		(void) printf("\n");

	/* make sure we didn't stop mid-delete */
	if ((delroot) || (delmddb)) {
		(void) mderror(ep, MDE_SYSTEM_FILE, sname);
		goto out;
	}

	/* flush stuff */
	if (doit) {
		if ((fflush(*tfp) != 0) ||
		    (fsync(fileno(*tfp)) != 0)) {
			(void) mdsyserror(ep, errno, *tname);
			goto out;
		}
	}

	/* return success */
	return (0);

	/* cleanup, return error */
out:
	if (fp != NULL)
		(void) fclose(fp);
	if (*tname != NULL) {
		(void) unlink(*tname);
		Free(*tname);
	}
	if (*tfp != NULL)
		(void) fclose(*tfp);
	return (-1);
}

/*
 * append root on MD lines to system
 */
int
meta_systemfile_append_mdroot(
	mdname_t	*rootnp,	/* root device name */
	char		*sname,		/* system file name */
	char		*tname,		/* temp file name */
	FILE		*tfp,		/* temp FILE */
	int		ismeta,		/* is a metadevice */
	int		doit,		/* really patch file */
	int		verbose,	/* show what we're doing */
	md_error_t	*ep
)
{
	char		*longblkname;

	/* check names */
	assert(sname != NULL);
	assert(tname != NULL);
	assert(!doit || tfp != NULL);

	/* get root /devices name */
	if ((longblkname = metagetdevicesname(rootnp, ep)) == NULL)
		return (-1);

	/* add header */
	if (verbose) {
		(void) printf(dgettext(TEXT_DOMAIN,
		    "Add the following lines to %s:\n\n"), sname);
		(void) printf("%s", BEGROOTSTR);
	}
	if (doit) {
		if (fprintf(tfp, "%s", BEGROOTSTR) == EOF) {
			return (mdsyserror(ep, errno, tname));
		}
	}

	/* add rootdev */
	if (ismeta) {
		if (verbose)
			(void) printf("rootdev:%s\n", longblkname);
		if (doit) {
			if (fprintf(tfp, "rootdev:%s\n", longblkname) == EOF) {
				return (mdsyserror(ep, errno, tname));
			}
		}
	}

	/* add trailer */
	if (verbose) {
		(void) printf("%s\n", ENDROOTSTR);
	}
	if (doit) {
		if (fprintf(tfp, "%s", ENDROOTSTR) == EOF) {
			return (mdsyserror(ep, errno, tname));
		}
	}

	/* flush stuff */
	if (doit) {
		if ((fflush(tfp) != 0) ||
		    (fsync(fileno(tfp)) != 0)) {
			return (mdsyserror(ep, errno, tname));
		}
	}

	/* return success */
	return (0);
}

/*
 * parse mddb.cf line
 *
 * Caller of this routine needs to free the device id string that
 * is passed back during a successful return.
 */
static int
confline(
	char		*line,		/* line in file */
	char		**driver,	/* returned driver name */
	minor_t		*mnump,		/* returned minor number */
	daddr_t		*block,		/* returned block offset */
	char		**devid_char_pp	/* returned device id string */
)
{
	char		*p = line;
	int		chksum = 0;
	int		i;
	uint_t		devid_size;

	if (*p == '#') {
		return (-1);
	}
	*driver = p;
	while ((*p != ' ') && (*p != '\t'))
		chksum += *p++;
	if (*driver == p) {
		return (-1);
	}
	*p++ = '\0';
	*mnump = strtoul(p, &p, 10);
	chksum += *mnump;
	*block = strtol(p, &p, 10);
	chksum += *block;

	/* parse out devid */
	while ((*p == ' ') || (*p == '\t')) {
		p++;
	}
	i = strcspn(p, " \t");
	*devid_char_pp = Malloc(i+1);
	(void) strncpy(*devid_char_pp, p, i);
	(*devid_char_pp)[i] = '\0';
	devid_size = i;
	p += devid_size;
	for (i = 0; i < devid_size; i++) {
		chksum += (*devid_char_pp)[i];
	}

	chksum += strtol(p, &p, 10);
	if (chksum != 42) {
		Free (*devid_char_pp);
		devid_char_pp = NULL;
		return (-1);
	}
	return (0);
}

/*
 * append MDDB lines to system
 */
int
meta_systemfile_append_mddb(
	char		*cname,		/* mddb.cf file name */
	char		*sname,		/* system file name */
	char		*tname,		/* temp file name */
	FILE		*tfp,		/* temp FILE */
	int		doit,		/* really patch file */
	int		verbose,	/* show what we're doing */
	int		check,		/* if set check that mddb.cf is not */
					/* empty before updating md.conf    */
	md_error_t	*ep		/* returned error */
)
{
	FILE		*cfp = NULL;
	char		buf[1024];
	char		*p;
	int		i;
	char		*driver;
	minor_t		mnum;
	daddr_t		block;
	char		line[MDDB_BOOTLIST_MAX_LEN];
	char		entry[MDDB_BOOTLIST_MAX_LEN];
	char		*devid_char_p = NULL;
	struct stat	statbuf;

	/* check names */
	assert(cname != NULL);
	assert(sname != NULL);
	assert(tname != NULL);
	assert(!doit || tfp != NULL);

	/* open database conf file */
	if ((cfp = fopen(cname, "r")) == NULL) {
		(void) mdsyserror(ep, errno, cname);
		goto out;
	}
	/* Check that it is an ordinary file */
	if (stat(cname, &statbuf) != 0) {
		(void) mdsyserror(ep, errno, cname);
		goto out;
	}
	if ((statbuf.st_mode & S_IFMT) != S_IFREG) {
		(void) mderror(ep, MDE_MDDB_FILE, cname);
		goto out;
	}

	/* add header */
	if (verbose) {
		(void) printf(dgettext(TEXT_DOMAIN,
		    "Add the following lines to %s:\n\n"), sname);
		(void) printf("%s", BEGMDDBSTR);
	}
	if (doit) {
		if (fprintf(tfp, "%s", BEGMDDBSTR) == EOF) {
			(void) mdsyserror(ep, errno, tname);
			goto out;
		}
	}

	/* append database lines */
	while (((p = fgets(buf, sizeof (buf), cfp)) != NULL) &&
	    (confline(buf, &driver, &mnum, &block, &devid_char_p) != 0))
		;
	/*
	 * It is possible to be in a state where the md_devid_destroy flag
	 * has been set and the mdmonitor service not be enabled on reboot
	 * such that metadevadm doesn't get run and the entries in mddb.cf
	 * recreated.  The following checks for this condition and will not
	 * allow an empty mddb.cf to overwrite md.conf and lose the users
	 * configuration
	 */
	if (check && p == NULL) {
		(void) mderror(ep, MDE_MDDB_FILE, cname);
		goto out;
	}

	for (i = 1; ((p != NULL) && (i <= MDDB_MAX_PATCH)); ++i) {
		(void) snprintf(line, sizeof (line),
		    "mddb_bootlist%d=\"%s:%lu:%ld:%s",
		    i, driver, mnum, block, devid_char_p);
		if (devid_char_p != NULL) {
			free(devid_char_p);
			devid_char_p = NULL;
		}

		while ((p = fgets(buf, sizeof (buf), cfp)) != NULL) {
			if (confline(buf, &driver, &mnum, &block,
			    &devid_char_p) != 0) {
				continue;
			}
			(void) snprintf(entry, sizeof (entry), " %s:%lu:%ld:%s",
			    driver, mnum, block, devid_char_p);

			if ((strlen(line) + strlen(entry) + 4) > sizeof (line))
				break;
			(void) strcat(line, entry);
			if (devid_char_p != NULL) {
				free(devid_char_p);
				devid_char_p = NULL;
			}
		}
		if (verbose)
			/* CSTYLED */
			(void) printf("%s\";\n", line);
		if (doit) {
			/* CSTYLED */
			if (fprintf(tfp, "%s\";\n", line) <= 0) {
				(void) mdsyserror(ep, errno, tname);
				goto out;
			}
		}
	}

	if (devid_char_p != NULL) {
		free(devid_char_p);
		devid_char_p = NULL;
	}

	/* add trailer */
	if (verbose)
		(void) printf("%s\n", ENDMDDBSTR);
	if (doit) {
		if (fprintf(tfp, "%s", ENDMDDBSTR) == EOF) {
			(void) mdsyserror(ep, errno, tname);
			goto out;
		}
	}

	/* close database conf file */
	if (fclose(cfp) != 0) {
		cfp = NULL;
		(void) mdsyserror(ep, errno, cname);
		goto out;
	}
	cfp = NULL;

	/* flush stuff */
	if (doit) {
		if ((fflush(tfp) != 0) ||
		    (fsync(fileno(tfp)) != 0)) {
			(void) mdsyserror(ep, errno, tname);
			goto out;
		}
	}

	/* return success */
	return (0);

	/* cleanup, return error */
out:
	if (cfp != NULL)
		(void) fclose(cfp);
	return (-1);
}