xref: /netbsd-src/sbin/raidctl/rf_configure.c (revision 5c922bdb9e488950241501cddeaf57ed944c0722)
1*5c922bdbSkre /*	$NetBSD: rf_configure.c,v 1.37 2022/07/21 09:19:53 kre Exp $ */
2340761f6Sthorpej 
3f675e35dSoster /*
4f675e35dSoster  * Copyright (c) 1995 Carnegie-Mellon University.
5f675e35dSoster  * All rights reserved.
6f675e35dSoster  *
7f675e35dSoster  * Author: Mark Holland
8f675e35dSoster  *
9f675e35dSoster  * Permission to use, copy, modify and distribute this software and
10f675e35dSoster  * its documentation is hereby granted, provided that both the copyright
11f675e35dSoster  * notice and this permission notice appear in all copies of the
12f675e35dSoster  * software, derivative works or modified versions, and any portions
13f675e35dSoster  * thereof, and that both notices appear in supporting documentation.
14f675e35dSoster  *
15f675e35dSoster  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
16f675e35dSoster  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
17f675e35dSoster  * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
18f675e35dSoster  *
19f675e35dSoster  * Carnegie Mellon requests users of this software to return to
20f675e35dSoster  *
21f675e35dSoster  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
22f675e35dSoster  *  School of Computer Science
23f675e35dSoster  *  Carnegie Mellon University
24f675e35dSoster  *  Pittsburgh PA 15213-3890
25f675e35dSoster  *
26f675e35dSoster  * any improvements or extensions that they make and grant Carnegie the
27f675e35dSoster  * rights to redistribute these changes.
28f675e35dSoster  */
29f675e35dSoster 
30f675e35dSoster /***************************************************************
31f675e35dSoster  *
32f675e35dSoster  * rf_configure.c -- code related to configuring the raidframe system
33f675e35dSoster  *
34f675e35dSoster  * configuration is complicated by the fact that we want the same
35f675e35dSoster  * driver to work both in the kernel and at user level.  In the
36f675e35dSoster  * kernel, we can't read the configuration file, so we configure
37f675e35dSoster  * by running a user-level program that reads the config file,
38f675e35dSoster  * creates a data structure describing the configuration and
39f675e35dSoster  * passes it into the kernel via an ioctl.  Since we want the config
40f675e35dSoster  * code to be common between the two versions of the driver, we
41f675e35dSoster  * configure using the same two-step process when running at
42f675e35dSoster  * user level.  Of course, at user level, the config structure is
43f675e35dSoster  * passed directly to the config routine, rather than via ioctl.
44f675e35dSoster  *
45f675e35dSoster  * This file is not compiled into the kernel, so we have no
46f675e35dSoster  * need for KERNEL ifdefs.
47f675e35dSoster  *
48f675e35dSoster  **************************************************************/
49c2a3b5ecSagc #include <sys/cdefs.h>
50c2a3b5ecSagc 
51c2a3b5ecSagc #ifndef lint
52*5c922bdbSkre __RCSID("$NetBSD: rf_configure.c,v 1.37 2022/07/21 09:19:53 kre Exp $");
53c2a3b5ecSagc #endif
54c2a3b5ecSagc 
55f675e35dSoster 
56f675e35dSoster #include <stdio.h>
57543c143bSoster #include <stdlib.h>
5893bf4aaeSmjacob #include <errno.h>
59543c143bSoster #include <strings.h>
6013a59406Schristos #include <err.h>
61cb7aeb82Skardel #include <util.h>
62b958b2dcSkre #include <assert.h>
63f675e35dSoster #include <sys/types.h>
64f675e35dSoster #include <sys/stat.h>
65541d521aSoster 
66541d521aSoster #include <dev/raidframe/raidframevar.h>
67541d521aSoster #include <dev/raidframe/raidframeio.h>
68f675e35dSoster #include "rf_configure.h"
69f675e35dSoster 
706937a5d5Schristos static char   *rf_find_non_white(char *, int);
716937a5d5Schristos static char   *rf_find_white(char *);
726937a5d5Schristos static int rf_search_file_for_start_of(const char *, char *, int, FILE *);
736937a5d5Schristos static int rf_get_next_nonblank_line(char *, int, FILE *, const char *);
74541d521aSoster 
756937a5d5Schristos #define RF_MIN(a,b) (((a) < (b)) ? (a) : (b))
766937a5d5Schristos 
776937a5d5Schristos static int     distSpareYes = 1;
786937a5d5Schristos static int     distSpareNo = 0;
79705631a2Soster 
8024b75d09Skre /*
8124b75d09Skre  * The mapsw[] table below contains all the various RAID types that might
8224b75d09Skre  * be supported by the kernel.  The actual supported types are found
8324b75d09Skre  * in sys/dev/raidframe/rf_layout.c.
8424b75d09Skre  */
85705631a2Soster 
866937a5d5Schristos static const RF_LayoutSW_t mapsw[] = {
87705631a2Soster 	/* parity declustering */
88705631a2Soster 	{'T', "Parity declustering",
89705631a2Soster 	 rf_MakeLayoutSpecificDeclustered, &distSpareNo},
90705631a2Soster 	/* parity declustering with distributed sparing */
91705631a2Soster 	{'D', "Distributed sparing parity declustering",
92705631a2Soster 	 rf_MakeLayoutSpecificDeclustered, &distSpareYes},
93705631a2Soster 	/* declustered P+Q */
94705631a2Soster 	{'Q', "Declustered P+Q",
95705631a2Soster 	 rf_MakeLayoutSpecificDeclustered, &distSpareNo},
96705631a2Soster 	/* RAID 5 with rotated sparing */
97705631a2Soster 	{'R', "RAID Level 5 rotated sparing", rf_MakeLayoutSpecificNULL, NULL},
98705631a2Soster 	/* Chained Declustering */
99705631a2Soster 	{'C', "Chained Declustering", rf_MakeLayoutSpecificNULL, NULL},
100705631a2Soster 	/* Interleaved Declustering */
101705631a2Soster 	{'I', "Interleaved Declustering", rf_MakeLayoutSpecificNULL, NULL},
102705631a2Soster 	/* RAID level 0 */
103705631a2Soster 	{'0', "RAID Level 0", rf_MakeLayoutSpecificNULL, NULL},
104705631a2Soster 	/* RAID level 1 */
105705631a2Soster 	{'1', "RAID Level 1", rf_MakeLayoutSpecificNULL, NULL},
106705631a2Soster 	/* RAID level 4 */
107705631a2Soster 	{'4', "RAID Level 4", rf_MakeLayoutSpecificNULL, NULL},
108705631a2Soster 	/* RAID level 5 */
109705631a2Soster 	{'5', "RAID Level 5", rf_MakeLayoutSpecificNULL, NULL},
110705631a2Soster 	/* Evenodd */
111705631a2Soster 	{'E', "EvenOdd", rf_MakeLayoutSpecificNULL, NULL},
112705631a2Soster 	/* Declustered Evenodd */
113705631a2Soster 	{'e', "Declustered EvenOdd",
114705631a2Soster 	 rf_MakeLayoutSpecificDeclustered, &distSpareNo},
115705631a2Soster 	/* parity logging */
116705631a2Soster 	{'L', "Parity logging", rf_MakeLayoutSpecificNULL, NULL},
117705631a2Soster 	/* end-of-list marker */
118705631a2Soster 	{'\0', NULL, NULL, NULL}
119705631a2Soster };
1206937a5d5Schristos 
1216937a5d5Schristos static const RF_LayoutSW_t *
rf_GetLayout(RF_ParityConfig_t parityConfig)122705631a2Soster rf_GetLayout(RF_ParityConfig_t parityConfig)
123705631a2Soster {
1246937a5d5Schristos 	const RF_LayoutSW_t *p;
125705631a2Soster 
126705631a2Soster 	/* look up the specific layout */
127705631a2Soster 	for (p = &mapsw[0]; p->parityConfig; p++)
128705631a2Soster 		if (p->parityConfig == parityConfig)
129705631a2Soster 			break;
130705631a2Soster 	if (!p->parityConfig)
1316937a5d5Schristos 		return NULL;
1326937a5d5Schristos 	return p;
133705631a2Soster }
134705631a2Soster 
135340761f6Sthorpej /*
136340761f6Sthorpej  * called from user level to read the configuration file and create
137f675e35dSoster  * a configuration control structure.  This is used in the user-level
138f675e35dSoster  * version of the driver, and in the user-level program that configures
139f675e35dSoster  * the system via ioctl.
140f675e35dSoster  */
141340761f6Sthorpej int
rf_MakeConfig(char * configname,RF_Config_t * cfgPtr)142f0121f1fSxtraeme rf_MakeConfig(char *configname, RF_Config_t *cfgPtr)
143f675e35dSoster {
144f2b04ca0Smrg 	int numscanned, val, c, retcode, aa, bb, cc;
1456937a5d5Schristos 	char buf[BUFSIZ], buf1[BUFSIZ], *cp;
1466937a5d5Schristos 	const RF_LayoutSW_t *lp;
147f675e35dSoster 	FILE *fp;
148f675e35dSoster 
1496937a5d5Schristos 	memset(cfgPtr, 0, sizeof(*cfgPtr));
150f675e35dSoster 
151f675e35dSoster 	fp = fopen(configname, "r");
152f675e35dSoster 	if (!fp) {
1536937a5d5Schristos 		warnx("Can't open config file %s", configname);
1546937a5d5Schristos 		return -1;
155f675e35dSoster 	}
156f675e35dSoster 	rewind(fp);
1576937a5d5Schristos 	if (rf_search_file_for_start_of("array", buf, sizeof(buf), fp)) {
1586937a5d5Schristos 		warnx("Unable to find start of \"array\" params in config "
1596937a5d5Schristos 		    "file %s", configname);
160340761f6Sthorpej 		retcode = -1;
161340761f6Sthorpej 		goto out;
162f675e35dSoster 	}
1636937a5d5Schristos 	rf_get_next_nonblank_line(buf, sizeof(buf), fp,
1646937a5d5Schristos 	    "Config file error (\"array\" section):  unable to get numRow "
1656937a5d5Schristos 	    "and numCol");
166340761f6Sthorpej 
167f675e35dSoster 	/*
168340761f6Sthorpej 	 * wackiness with aa, bb, cc to get around size problems on
169340761f6Sthorpej 	 * different platforms
170f675e35dSoster 	 */
171f2b04ca0Smrg 
172f2b04ca0Smrg 	/*
173f2b04ca0Smrg 	 * Allow both "numCol numSpare" as well as old-style
174f2b04ca0Smrg 	 * "numRow numCol numSpare".
175f2b04ca0Smrg 	 * Note that numRow has always been ignored.
176f2b04ca0Smrg 	 */
177f675e35dSoster 	numscanned = sscanf(buf, "%d %d %d", &aa, &bb, &cc);
178f675e35dSoster 	if (numscanned != 3) {
179f2b04ca0Smrg 		numscanned = sscanf(buf, "%d %d", &bb, &cc);
180f2b04ca0Smrg 		if (numscanned != 2) {
181f2b04ca0Smrg 			warnx("Config file error (\"array\" section): unable "
182f2b04ca0Smrg 			    "to get numCol, numSpare");
183340761f6Sthorpej 			retcode = -1;
184340761f6Sthorpej 			goto out;
185f675e35dSoster 		}
186f2b04ca0Smrg 	}
187f675e35dSoster 	cfgPtr->numCol = (RF_RowCol_t) bb;
188f675e35dSoster 	cfgPtr->numSpare = (RF_RowCol_t) cc;
189f675e35dSoster 
190f675e35dSoster 	/* debug section is optional */
191f675e35dSoster 	for (c = 0; c < RF_MAXDBGV; c++)
192f675e35dSoster 		cfgPtr->debugVars[c][0] = '\0';
193f675e35dSoster 	rewind(fp);
1946937a5d5Schristos 	if (!rf_search_file_for_start_of("debug", buf, sizeof(buf), fp)) {
195f675e35dSoster 		for (c = 0; c < RF_MAXDBGV; c++) {
1966937a5d5Schristos 			if (rf_get_next_nonblank_line(buf, sizeof(buf), fp,
1976937a5d5Schristos 			    NULL))
198340761f6Sthorpej 				break;
1996937a5d5Schristos 			cp = rf_find_non_white(buf, 0);
2006937a5d5Schristos 			if (!strncmp(cp, "START", sizeof("START") - 1))
201340761f6Sthorpej 				break;
202dbab1374Schristos 			(void) strlcpy(cfgPtr->debugVars[c], cp,
2030b04aee3Sitojun 			    sizeof(cfgPtr->debugVars[c]));
204f675e35dSoster 		}
205f675e35dSoster 	}
206f675e35dSoster 	rewind(fp);
2070b04aee3Sitojun 	strlcpy(cfgPtr->diskQueueType, "fifo", sizeof(cfgPtr->diskQueueType));
208f675e35dSoster 	cfgPtr->maxOutstandingDiskReqs = 1;
20924b75d09Skre 
210f675e35dSoster 	/* scan the file for the block related to disk queues */
2116937a5d5Schristos 	if (rf_search_file_for_start_of("queue", buf, sizeof(buf), fp) ||
2126937a5d5Schristos 	    rf_get_next_nonblank_line(buf, sizeof(buf), fp, NULL)) {
2136937a5d5Schristos 		warnx("[No disk queue discipline specified in config file %s. "
2146937a5d5Schristos 		    "Using %s.]", configname, cfgPtr->diskQueueType);
215f675e35dSoster 	}
216b958b2dcSkre     else {
217f675e35dSoster 
21824b75d09Skre 	/*
21924b75d09Skre 	 * the queue specifier line contains two entries: 1st char of first
220340761f6Sthorpej 	 * word specifies queue to be used 2nd word specifies max num reqs
22124b75d09Skre 	 * that can be outstanding on the disk itself (typically 1)
22224b75d09Skre 	 */
223b958b2dcSkre 	assert(64 < sizeof buf1);
224b958b2dcSkre 	if (sscanf(buf, "%64s %d", buf1, &val) != 2 || strlen(buf1) >= 60) {
2256937a5d5Schristos 		warnx("Can't determine queue type and/or max outstanding "
226b958b2dcSkre 		    "reqs from line: %.*s", (int)(sizeof(buf) - 1), buf);
2276937a5d5Schristos 		warnx("Using %s-%d", cfgPtr->diskQueueType,
2286937a5d5Schristos 		    cfgPtr->maxOutstandingDiskReqs);
229f675e35dSoster 	} else {
230b958b2dcSkre #if 0
231a53c712bSthorpej 		char *ch;
232b958b2dcSkre #endif
2336937a5d5Schristos 		memcpy(cfgPtr->diskQueueType, buf1,
234340761f6Sthorpej 		    RF_MIN(sizeof(cfgPtr->diskQueueType), strlen(buf1) + 1));
235b958b2dcSkre 		cfgPtr->diskQueueType[sizeof cfgPtr->diskQueueType - 1] = '\0';
236b958b2dcSkre 
237b958b2dcSkre #if 0	/* this indicates a lack of understanding of how scanf() works */
238b958b2dcSkre 	/* it was also pointless as buf1 data was (b4) never used again */
239a53c712bSthorpej 		for (ch = buf1; *ch; ch++) {
240a53c712bSthorpej 			if (*ch == ' ') {
241a53c712bSthorpej 				*ch = '\0';
242f675e35dSoster 				break;
243f675e35dSoster 			}
244f675e35dSoster 		}
245b958b2dcSkre #endif
246f675e35dSoster 		cfgPtr->maxOutstandingDiskReqs = val;
247b958b2dcSkre 		if (cfgPtr->maxOutstandingDiskReqs != val) {
248b958b2dcSkre 			warnx("Queue length for %s out of range"
249b958b2dcSkre 			    " [%d interp as %d], assuming 1",
250b958b2dcSkre 			    buf1, val, cfgPtr->maxOutstandingDiskReqs);
251b958b2dcSkre 			cfgPtr->maxOutstandingDiskReqs = 1;
252b958b2dcSkre 		}
253b958b2dcSkre 	}
254f675e35dSoster     }
255f675e35dSoster 
256f675e35dSoster 	rewind(fp);
257f675e35dSoster 
2586937a5d5Schristos 	if (rf_search_file_for_start_of("disks", buf, sizeof(buf), fp)) {
2596937a5d5Schristos 		warnx("Can't find \"disks\" section in config file %s",
2606937a5d5Schristos 		    configname);
261340761f6Sthorpej 		retcode = -1;
262340761f6Sthorpej 		goto out;
263f675e35dSoster 	}
264f675e35dSoster 	for (c = 0; c < cfgPtr->numCol; c++) {
2656937a5d5Schristos 		char b1[MAXPATHLEN];
266cb7aeb82Skardel 		const char *b;
267cb7aeb82Skardel 
268340761f6Sthorpej 		if (rf_get_next_nonblank_line(
269cb7aeb82Skardel 		    buf, sizeof(buf), fp, NULL)) {
2705a1ff55aSkre 			warnx("Config file error: unable to find device "
2715a1ff55aSkre 			    "file name for disk at col %d", c);
272340761f6Sthorpej 			retcode = -1;
273340761f6Sthorpej 			goto out;
274f675e35dSoster 		}
275cb7aeb82Skardel 
276cb7aeb82Skardel 		b = getfsspecname(b1, sizeof(b1), buf);
277cb7aeb82Skardel 		if (b == NULL) {
2786937a5d5Schristos 			warnx("Config file error: warning: unable to "
2795a1ff55aSkre 			    "get device file for disk at col %d: %s",
2805a1ff55aSkre 			    c, b1);
281*5c922bdbSkre 			b = "absent";
282cb7aeb82Skardel 		}
283cb7aeb82Skardel 
284f2b04ca0Smrg 		strlcpy(cfgPtr->devnames[0][c], b,
285f2b04ca0Smrg 		    sizeof(cfgPtr->devnames[0][c]));
286f675e35dSoster 	}
287f675e35dSoster 
288f675e35dSoster 	/* "spare" section is optional */
289f675e35dSoster 	rewind(fp);
2906937a5d5Schristos 	if (rf_search_file_for_start_of("spare", buf, sizeof(buf), fp))
291340761f6Sthorpej 		cfgPtr->numSpare = 0;
292f675e35dSoster 	for (c = 0; c < cfgPtr->numSpare; c++) {
2936937a5d5Schristos 		char b1[MAXPATHLEN];
294cb7aeb82Skardel 		const char *b;
295cb7aeb82Skardel 
2966937a5d5Schristos 		if (rf_get_next_nonblank_line(buf, sizeof(buf), fp, NULL)) {
2976937a5d5Schristos 			warnx("Config file error: unable to get device file "
2986937a5d5Schristos 			    "for spare disk %d", c);
299340761f6Sthorpej 			retcode = -1;
300340761f6Sthorpej 			goto out;
301f675e35dSoster 		}
302cb7aeb82Skardel 
303cb7aeb82Skardel 		b = getfsspecname(b1, sizeof(b1), buf);
304cb7aeb82Skardel 		if (b == NULL) {
3056937a5d5Schristos 			warnx("Config file error: warning: unable to get "
30607b516d2Smrg 			    "device file for spare disk %d: %s", c, buf);
307cb7aeb82Skardel 			b = buf;
308cb7aeb82Skardel 		}
309cb7aeb82Skardel 
310f2b04ca0Smrg 	        strlcpy(cfgPtr->spare_names[c], b,
311f2b04ca0Smrg 		    sizeof(cfgPtr->spare_names[c]));
312f675e35dSoster 	}
313f675e35dSoster 
314f675e35dSoster 	/* scan the file for the block related to layout */
315f675e35dSoster 	rewind(fp);
3166937a5d5Schristos 	if (rf_search_file_for_start_of("layout", buf, sizeof(buf), fp)) {
3176937a5d5Schristos 		warnx("Can't find \"layout\" section in configuration file %s",
3186937a5d5Schristos 		    configname);
319340761f6Sthorpej 		retcode = -1;
320340761f6Sthorpej 		goto out;
321f675e35dSoster 	}
3226937a5d5Schristos 	if (rf_get_next_nonblank_line(buf, sizeof(buf), fp, NULL)) {
3236937a5d5Schristos 		warnx("Config file error (\"layout\" section): unable to find "
3246937a5d5Schristos 		    "common layout param line");
325340761f6Sthorpej 		retcode = -1;
326340761f6Sthorpej 		goto out;
327f675e35dSoster 	}
328f675e35dSoster 	c = sscanf(buf, "%d %d %d %c", &aa, &bb, &cc, &cfgPtr->parityConfig);
329f675e35dSoster 	cfgPtr->sectPerSU = (RF_SectorNum_t) aa;
330f675e35dSoster 	cfgPtr->SUsPerPU = (RF_StripeNum_t) bb;
331f675e35dSoster 	cfgPtr->SUsPerRU = (RF_StripeNum_t) cc;
332f675e35dSoster 	if (c != 4) {
3336937a5d5Schristos 		warnx("Unable to scan common layout line");
334340761f6Sthorpej 		retcode = -1;
335340761f6Sthorpej 		goto out;
336f675e35dSoster 	}
337f675e35dSoster 	lp = rf_GetLayout(cfgPtr->parityConfig);
338f675e35dSoster 	if (lp == NULL) {
3396937a5d5Schristos 		warnx("Unknown parity config '%c'",
340340761f6Sthorpej 		    cfgPtr->parityConfig);
341f675e35dSoster 		retcode = -1;
342f675e35dSoster 		goto out;
343f675e35dSoster 	}
344f675e35dSoster 
3456937a5d5Schristos 	retcode = lp->MakeLayoutSpecific(fp, cfgPtr,
3466937a5d5Schristos 	    lp->makeLayoutSpecificArg);
347f675e35dSoster out:
348f675e35dSoster 	fclose(fp);
349f675e35dSoster 	if (retcode < 0)
350f675e35dSoster 		retcode = errno = EINVAL;
351f675e35dSoster 	else
352f675e35dSoster 		errno = retcode;
3536937a5d5Schristos 	return retcode;
354f675e35dSoster }
355f675e35dSoster 
356f675e35dSoster 
35724b75d09Skre /*
35824b75d09Skre  * used in architectures such as RAID0 where there is no layout-specific
359f675e35dSoster  * information to be passed into the configuration code.
360f675e35dSoster  */
361340761f6Sthorpej int
rf_MakeLayoutSpecificNULL(FILE * fp,RF_Config_t * cfgPtr,void * ignored)362f0121f1fSxtraeme rf_MakeLayoutSpecificNULL(FILE *fp, RF_Config_t *cfgPtr, void *ignored)
363f675e35dSoster {
364f675e35dSoster 	cfgPtr->layoutSpecificSize = 0;
365f675e35dSoster 	cfgPtr->layoutSpecific = NULL;
3666937a5d5Schristos 	return 0;
367f675e35dSoster }
368f675e35dSoster 
369340761f6Sthorpej int
rf_MakeLayoutSpecificDeclustered(FILE * configfp,RF_Config_t * cfgPtr,void * arg)370f0121f1fSxtraeme rf_MakeLayoutSpecificDeclustered(FILE *configfp, RF_Config_t *cfgPtr, void *arg)
371f675e35dSoster {
372f675e35dSoster 	int b, v, k, r, lambda, norotate, i, val, distSpare;
373f675e35dSoster 	char *cfgBuf, *bdfile, *p, *smname;
3746937a5d5Schristos 	char buf[BUFSIZ], smbuf[BUFSIZ];
375f675e35dSoster 	FILE *fp;
376f675e35dSoster 
377f675e35dSoster 	distSpare = *((int *) arg);
378f675e35dSoster 
379f675e35dSoster 	/* get the block design file name */
3806937a5d5Schristos 	if (rf_get_next_nonblank_line(buf, sizeof(buf), configfp,
3816937a5d5Schristos 	    "Can't find block design file name in config file"))
3826937a5d5Schristos 		return EINVAL;
3836937a5d5Schristos 	bdfile = rf_find_non_white(buf, 1);
384f675e35dSoster 	/* open bd file, check validity of configuration */
385f675e35dSoster 	if ((fp = fopen(bdfile, "r")) == NULL) {
3866937a5d5Schristos 		warn("RAID: config error: Can't open layout table file %s",
3876937a5d5Schristos 		    bdfile);
3886937a5d5Schristos 		return EINVAL;
389f675e35dSoster 	}
3906937a5d5Schristos 	if (fgets(buf, sizeof(buf), fp) == NULL) {
3916937a5d5Schristos 		warnx("RAID: config error: Can't read layout from layout "
3926937a5d5Schristos 		    "table file %s", bdfile);
3931fcee3dbSdan 		fclose(fp);
3946937a5d5Schristos 		return EINVAL;
395944d6ad3Swiz 	}
3966937a5d5Schristos 	i = sscanf(buf, "%u %u %u %u %u %u",
3976937a5d5Schristos 	    &b, &v, &k, &r, &lambda, &norotate);
398f675e35dSoster 	if (i == 5)
399f675e35dSoster 		norotate = 0;	/* no-rotate flag is optional */
400f675e35dSoster 	else if (i != 6) {
4016937a5d5Schristos 		warnx("Unable to parse header line in block design file");
4021fcee3dbSdan 		fclose(fp);
4036937a5d5Schristos 		return EINVAL;
404f675e35dSoster 	}
40524b75d09Skre 	/*
40624b75d09Skre 	 * set the sparemap directory.  In the in-kernel version, there's a
40724b75d09Skre 	 * daemon that's responsible for finding the sparemaps
40824b75d09Skre 	 */
409f675e35dSoster 	if (distSpare) {
41070c1fd98Skre 		if (rf_get_next_nonblank_line(smbuf, sizeof(smbuf), configfp,
4116937a5d5Schristos 		    "Can't find sparemap file name in config file")) {
4121fcee3dbSdan 			fclose(fp);
4136937a5d5Schristos 			return EINVAL;
4141fcee3dbSdan 		}
4156937a5d5Schristos 		smname = rf_find_non_white(smbuf, 1);
41670c1fd98Skre 		if (strlen(smname) >= RF_SPAREMAP_NAME_LEN) {
41770c1fd98Skre 			warnx("sparemap file name '%s' too long (max %d)",
41870c1fd98Skre 			    smname, RF_SPAREMAP_NAME_LEN - 1);
41970c1fd98Skre 			fclose(fp);
42070c1fd98Skre 			return EINVAL;
42170c1fd98Skre 		}
422340761f6Sthorpej 	} else {
423f675e35dSoster 		smbuf[0] = '\0';
424f675e35dSoster 		smname = smbuf;
425f675e35dSoster 	}
426f675e35dSoster 
427f675e35dSoster 	/* allocate a buffer to hold the configuration info */
428340761f6Sthorpej 	cfgPtr->layoutSpecificSize = RF_SPAREMAP_NAME_LEN +
429340761f6Sthorpej 	    6 * sizeof(int) + b * k;
43042abd8f7Soster 
431f675e35dSoster 	cfgBuf = (char *) malloc(cfgPtr->layoutSpecificSize);
4321fcee3dbSdan 	if (cfgBuf == NULL) {
4331fcee3dbSdan 		fclose(fp);
4346937a5d5Schristos 		return ENOMEM;
4351fcee3dbSdan 	}
436f675e35dSoster 	cfgPtr->layoutSpecific = (void *) cfgBuf;
437f675e35dSoster 	p = cfgBuf;
438f675e35dSoster 
439f675e35dSoster 	/* install name of sparemap file */
440f675e35dSoster 	for (i = 0; smname[i]; i++)
441f675e35dSoster 		*p++ = smname[i];
442f675e35dSoster 	/* pad with zeros */
443f675e35dSoster 	while (i < RF_SPAREMAP_NAME_LEN) {
444f675e35dSoster 		*p++ = '\0';
445f675e35dSoster 		i++;
446f675e35dSoster 	}
44770c1fd98Skre 	if ((i & (sizeof(int) - 1)) != 0) {
44870c1fd98Skre 		/* panic, unaligned data; RF_SPAREMAP_NAME_LEN invalid */
44970c1fd98Skre 		warnx("Program Bug: (RF_SPAREMAP_NAME_LEN(%d) %% %zd) != 0",
45070c1fd98Skre 		    RF_SPAREMAP_NAME_LEN, sizeof(int));
45170c1fd98Skre 		fclose(fp);
45270c1fd98Skre 		return EINVAL;
45370c1fd98Skre 	}
454f675e35dSoster 
455f675e35dSoster 	/*
456f675e35dSoster 	 * fill in the buffer with the block design parameters
457f675e35dSoster 	 * and then the block design itself
458f675e35dSoster 	 */
459340761f6Sthorpej 	*((int *) p) = b;
460340761f6Sthorpej 	p += sizeof(int);
461340761f6Sthorpej 	*((int *) p) = v;
462340761f6Sthorpej 	p += sizeof(int);
463340761f6Sthorpej 	*((int *) p) = k;
464340761f6Sthorpej 	p += sizeof(int);
465340761f6Sthorpej 	*((int *) p) = r;
466340761f6Sthorpej 	p += sizeof(int);
467340761f6Sthorpej 	*((int *) p) = lambda;
468340761f6Sthorpej 	p += sizeof(int);
469340761f6Sthorpej 	*((int *) p) = norotate;
470340761f6Sthorpej 	p += sizeof(int);
471f675e35dSoster 
472f675e35dSoster 	while (fscanf(fp, "%d", &val) == 1)
473f675e35dSoster 		*p++ = (char) val;
474f675e35dSoster 	fclose(fp);
475d4ab6626Slukem 	if ((unsigned int)(p - cfgBuf) != cfgPtr->layoutSpecificSize) {
4766937a5d5Schristos 		warnx("Size mismatch creating layout specific data: is %tu sb "
4776937a5d5Schristos 		    "%zu bytes", p - cfgBuf, 6 * sizeof(int) + b * k);
4786937a5d5Schristos 		return EINVAL;
479f675e35dSoster 	}
4806937a5d5Schristos 	return 0;
481f675e35dSoster }
482f675e35dSoster 
483f675e35dSoster /****************************************************************************
484f675e35dSoster  *
485f675e35dSoster  * utilities
486f675e35dSoster  *
487f675e35dSoster  ***************************************************************************/
488f675e35dSoster 
489180bbfd8Soster /* finds a non-white character in the line */
4906937a5d5Schristos static char *
rf_find_non_white(char * p,int eatnl)4916937a5d5Schristos rf_find_non_white(char *p, int eatnl)
492180bbfd8Soster {
49370c1fd98Skre 	while (*p == ' ' || *p == '\t')
49470c1fd98Skre 		p++;
4956937a5d5Schristos 	if (*p == '\n' && eatnl)
4966937a5d5Schristos 		*p = '\0';
4976937a5d5Schristos 	return p;
498180bbfd8Soster }
499180bbfd8Soster 
500180bbfd8Soster /* finds a white character in the line */
5016937a5d5Schristos static char *
rf_find_white(char * p)502180bbfd8Soster rf_find_white(char *p)
503180bbfd8Soster {
50470c1fd98Skre 	while (*p != '\0' && *p != ' ' && *p != '\t')
50570c1fd98Skre 		p++;
5066937a5d5Schristos 	return p;
507180bbfd8Soster }
508180bbfd8Soster 
509340761f6Sthorpej /*
510340761f6Sthorpej  * searches a file for a line that says "START string", where string is
511f675e35dSoster  * specified as a parameter
512f675e35dSoster  */
513340761f6Sthorpej static int
rf_search_file_for_start_of(const char * string,char * buf,int len,FILE * fp)514f0121f1fSxtraeme rf_search_file_for_start_of(const char *string, char *buf, int len, FILE *fp)
515f675e35dSoster {
516f675e35dSoster 	char *p;
517f675e35dSoster 
518f675e35dSoster 	while (1) {
519340761f6Sthorpej 		if (fgets(buf, len, fp) == NULL)
5206937a5d5Schristos 			return -1;
5216937a5d5Schristos 		p = rf_find_non_white(buf, 0);
522f675e35dSoster 		if (!strncmp(p, "START", strlen("START"))) {
523f675e35dSoster 			p = rf_find_white(p);
5246937a5d5Schristos 			p = rf_find_non_white(p, 0);
525340761f6Sthorpej 			if (!strncmp(p, string, strlen(string)))
5266937a5d5Schristos 				return 0;
527f675e35dSoster 		}
528f675e35dSoster 	}
529f675e35dSoster }
530f675e35dSoster 
531f675e35dSoster /* reads from file fp into buf until it finds an interesting line */
5326937a5d5Schristos static int
rf_get_next_nonblank_line(char * buf,int len,FILE * fp,const char * errmsg)533f0121f1fSxtraeme rf_get_next_nonblank_line(char *buf, int len, FILE *fp, const char *errmsg)
534f675e35dSoster {
535f675e35dSoster 	char *p;
53670c1fd98Skre 	size_t l;
537f675e35dSoster 
5382b7fd022Soster 	while (fgets(buf, len, fp) != NULL) {
5396937a5d5Schristos 		p = rf_find_non_white(buf, 0);
540340761f6Sthorpej 		if (*p == '\n' || *p == '\0' || *p == '#')
541340761f6Sthorpej 			continue;
54270c1fd98Skre 		l = strlen(buf);
54370c1fd98Skre 		while (l > 0 && (buf[--l] == ' ' || buf[l] == '\n'))
544fa730392Soster 			buf[l] = '\0';
5456937a5d5Schristos 		return 0;
546f675e35dSoster 	}
547340761f6Sthorpej 	if (errmsg)
5486937a5d5Schristos 		warnx("%s", errmsg);
5496937a5d5Schristos 	return 1;
550f675e35dSoster }
551f675e35dSoster 
552340761f6Sthorpej /*
553340761f6Sthorpej  * Allocates an array for the spare table, and initializes it from a file.
554f675e35dSoster  * In the user-level version, this is called when recon is initiated.
555f675e35dSoster  * When/if I move recon into the kernel, there'll be a daemon that does
556f675e35dSoster  * an ioctl into raidframe which will block until a spare table is needed.
557f675e35dSoster  * When it returns, it will read a spare table from the file system,
558f675e35dSoster  * pass it into the kernel via a different ioctl, and then block again
559f675e35dSoster  * on the original ioctl.
560f675e35dSoster  *
561f675e35dSoster  * This is specific to the declustered layout, but doesn't belong in
562f675e35dSoster  * rf_decluster.c because it uses stuff that can't be compiled into
563f675e35dSoster  * the kernel, and it needs to be compiled into the user-level sparemap daemon.
564f675e35dSoster  */
565340761f6Sthorpej void *
rf_ReadSpareTable(RF_SparetWait_t * req,char * fname)566f0121f1fSxtraeme rf_ReadSpareTable(RF_SparetWait_t *req, char *fname)
567f675e35dSoster {
568340761f6Sthorpej 	int i, j, numFound, linecount, tableNum, tupleNum,
569340761f6Sthorpej 	    spareDisk, spareBlkOffset;
5706937a5d5Schristos 	char buf[BUFSIZ], targString[BUFSIZ], errString[BUFSIZ];
571f675e35dSoster 	RF_SpareTableEntry_t **table;
572c5f07454Schristos 	FILE *fp = NULL;
57370c1fd98Skre 	size_t len;
574f675e35dSoster 
575f675e35dSoster 	/* allocate and initialize the table */
576c5f07454Schristos 	table = calloc(req->TablesPerSpareRegion, sizeof(*table));
57742abd8f7Soster 	if (table == NULL) {
578c5f07454Schristos 		warn("%s: Unable to allocate table", __func__);
579c5f07454Schristos 		return NULL;
58042abd8f7Soster 	}
581f675e35dSoster 	for (i = 0; i < req->TablesPerSpareRegion; i++) {
582c5f07454Schristos 		table[i] = calloc(req->BlocksPerTable, sizeof(**table));
58342abd8f7Soster 		if (table[i] == NULL) {
58470c1fd98Skre 			warn("%s: Unable to allocate table:%d", __func__, i);
585c5f07454Schristos 			goto out;
58642abd8f7Soster 		}
587340761f6Sthorpej 		for (j = 0; j < req->BlocksPerTable; j++)
588340761f6Sthorpej 			table[i][j].spareDisk =
589340761f6Sthorpej 			    table[i][j].spareBlockOffsetInSUs = -1;
590f675e35dSoster 	}
591f675e35dSoster 
592f675e35dSoster 	/* 2.  open sparemap file, sanity check */
593f675e35dSoster 	if ((fp = fopen(fname, "r")) == NULL) {
594c5f07454Schristos 		warn("%s: Can't open sparemap file %s", __func__, fname);
595c5f07454Schristos 		goto out;
596f675e35dSoster 	}
597340761f6Sthorpej 	if (rf_get_next_nonblank_line(buf, 1024, fp,
5986937a5d5Schristos 	    "Invalid sparemap file:  can't find header line"))
599c5f07454Schristos 		goto out;
600c5f07454Schristos 
60170c1fd98Skre 	len = strlen(buf);
602c5f07454Schristos 	if (len != 0 && buf[len - 1] == '\n')
603c5f07454Schristos 		buf[len - 1] = '\0';
604f675e35dSoster 
6050b04aee3Sitojun 	snprintf(targString, sizeof(targString), "fdisk %d\n", req->fcol);
6060b04aee3Sitojun 	snprintf(errString, sizeof(errString),
6076937a5d5Schristos 	    "Invalid sparemap file: Can't find \"fdisk %d\" line", req->fcol);
608c5f07454Schristos 	for (;;) {
6096937a5d5Schristos 		rf_get_next_nonblank_line(buf, sizeof(buf), fp, errString);
610340761f6Sthorpej 		if (!strncmp(buf, targString, strlen(targString)))
611340761f6Sthorpej 			break;
612f675e35dSoster 	}
613f675e35dSoster 
614f675e35dSoster 	/* no more blank lines or comments allowed now */
615f675e35dSoster 	linecount = req->TablesPerSpareRegion * req->TableDepthInPUs;
616f675e35dSoster 	for (i = 0; i < linecount; i++) {
61770c1fd98Skre 		char linebuf[BUFSIZ];
61870c1fd98Skre 
61970c1fd98Skre 		if (fgets(linebuf, BUFSIZ, fp) == NULL) {
62013a59406Schristos 			warnx("Sparemap file prematurely exhausted after %d "
62113a59406Schristos 			    "of %d lines", i, linecount);
622c5f07454Schristos 			goto out;
623f675e35dSoster 		}
62470c1fd98Skre 		numFound = sscanf(linebuf, " %d %d %d %d", &tableNum, &tupleNum,
62570c1fd98Skre 		    &spareDisk, &spareBlkOffset);
62670c1fd98Skre 		if (numFound != 4) {
62770c1fd98Skre 			warnx("Sparemap file format error - "
62870c1fd98Skre 			    "line %d of %d lines",
62970c1fd98Skre 			    i + 1, linecount);
63070c1fd98Skre 			goto out;
63170c1fd98Skre 		}
632f675e35dSoster 
633f675e35dSoster 		table[tableNum][tupleNum].spareDisk = spareDisk;
634340761f6Sthorpej 		table[tableNum][tupleNum].spareBlockOffsetInSUs =
635340761f6Sthorpej 		    spareBlkOffset * req->SUsPerPU;
636f675e35dSoster 	}
637f675e35dSoster 
638f675e35dSoster 	fclose(fp);
6396937a5d5Schristos 	return (void *) table;
640c5f07454Schristos out:
641c5f07454Schristos 	if (fp)
642c5f07454Schristos 		fclose(fp);
643c5f07454Schristos 	for (i = 0; i < req->TablesPerSpareRegion; i++)
644c5f07454Schristos 		free(table[i]);
645c5f07454Schristos 	free(table);
646c5f07454Schristos 	return NULL;
647f675e35dSoster }
648