xref: /netbsd-src/sbin/raidctl/rf_configure.c (revision 181254a7b1bdde6873432bffef2d2decc4b5c22f)
1 /*	$NetBSD: rf_configure.c,v 1.33 2018/01/18 00:32:49 mrg Exp $ */
2 
3 /*
4  * Copyright (c) 1995 Carnegie-Mellon University.
5  * All rights reserved.
6  *
7  * Author: Mark Holland
8  *
9  * Permission to use, copy, modify and distribute this software and
10  * its documentation is hereby granted, provided that both the copyright
11  * notice and this permission notice appear in all copies of the
12  * software, derivative works or modified versions, and any portions
13  * thereof, and that both notices appear in supporting documentation.
14  *
15  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
16  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
17  * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
18  *
19  * Carnegie Mellon requests users of this software to return to
20  *
21  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
22  *  School of Computer Science
23  *  Carnegie Mellon University
24  *  Pittsburgh PA 15213-3890
25  *
26  * any improvements or extensions that they make and grant Carnegie the
27  * rights to redistribute these changes.
28  */
29 
30 /***************************************************************
31  *
32  * rf_configure.c -- code related to configuring the raidframe system
33  *
34  * configuration is complicated by the fact that we want the same
35  * driver to work both in the kernel and at user level.  In the
36  * kernel, we can't read the configuration file, so we configure
37  * by running a user-level program that reads the config file,
38  * creates a data structure describing the configuration and
39  * passes it into the kernel via an ioctl.  Since we want the config
40  * code to be common between the two versions of the driver, we
41  * configure using the same two-step process when running at
42  * user level.  Of course, at user level, the config structure is
43  * passed directly to the config routine, rather than via ioctl.
44  *
45  * This file is not compiled into the kernel, so we have no
46  * need for KERNEL ifdefs.
47  *
48  **************************************************************/
49 #include <sys/cdefs.h>
50 
51 #ifndef lint
52 __RCSID("$NetBSD: rf_configure.c,v 1.33 2018/01/18 00:32:49 mrg Exp $");
53 #endif
54 
55 
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <errno.h>
59 #include <strings.h>
60 #include <err.h>
61 #include <util.h>
62 #include <sys/types.h>
63 #include <sys/stat.h>
64 
65 #include <dev/raidframe/raidframevar.h>
66 #include <dev/raidframe/raidframeio.h>
67 #include "rf_configure.h"
68 
69 static char   *rf_find_non_white(char *, int);
70 static char   *rf_find_white(char *);
71 static int rf_search_file_for_start_of(const char *, char *, int, FILE *);
72 static int rf_get_next_nonblank_line(char *, int, FILE *, const char *);
73 
74 #define RF_MIN(a,b) (((a) < (b)) ? (a) : (b))
75 
76 static int     distSpareYes = 1;
77 static int     distSpareNo = 0;
78 
79 /*
80  * The mapsw[] table below contains all the various RAID types that might
81  * be supported by the kernel.  The actual supported types are found
82  * in sys/dev/raidframe/rf_layout.c.
83  */
84 
85 static const RF_LayoutSW_t mapsw[] = {
86 	/* parity declustering */
87 	{'T', "Parity declustering",
88 	 rf_MakeLayoutSpecificDeclustered, &distSpareNo},
89 	/* parity declustering with distributed sparing */
90 	{'D', "Distributed sparing parity declustering",
91 	 rf_MakeLayoutSpecificDeclustered, &distSpareYes},
92 	/* declustered P+Q */
93 	{'Q', "Declustered P+Q",
94 	 rf_MakeLayoutSpecificDeclustered, &distSpareNo},
95 	/* RAID 5 with rotated sparing */
96 	{'R', "RAID Level 5 rotated sparing", rf_MakeLayoutSpecificNULL, NULL},
97 	/* Chained Declustering */
98 	{'C', "Chained Declustering", rf_MakeLayoutSpecificNULL, NULL},
99 	/* Interleaved Declustering */
100 	{'I', "Interleaved Declustering", rf_MakeLayoutSpecificNULL, NULL},
101 	/* RAID level 0 */
102 	{'0', "RAID Level 0", rf_MakeLayoutSpecificNULL, NULL},
103 	/* RAID level 1 */
104 	{'1', "RAID Level 1", rf_MakeLayoutSpecificNULL, NULL},
105 	/* RAID level 4 */
106 	{'4', "RAID Level 4", rf_MakeLayoutSpecificNULL, NULL},
107 	/* RAID level 5 */
108 	{'5', "RAID Level 5", rf_MakeLayoutSpecificNULL, NULL},
109 	/* Evenodd */
110 	{'E', "EvenOdd", rf_MakeLayoutSpecificNULL, NULL},
111 	/* Declustered Evenodd */
112 	{'e', "Declustered EvenOdd",
113 	 rf_MakeLayoutSpecificDeclustered, &distSpareNo},
114 	/* parity logging */
115 	{'L', "Parity logging", rf_MakeLayoutSpecificNULL, NULL},
116 	/* end-of-list marker */
117 	{'\0', NULL, NULL, NULL}
118 };
119 
120 static const RF_LayoutSW_t *
121 rf_GetLayout(RF_ParityConfig_t parityConfig)
122 {
123 	const RF_LayoutSW_t *p;
124 
125 	/* look up the specific layout */
126 	for (p = &mapsw[0]; p->parityConfig; p++)
127 		if (p->parityConfig == parityConfig)
128 			break;
129 	if (!p->parityConfig)
130 		return NULL;
131 	return p;
132 }
133 
134 /*
135  * called from user level to read the configuration file and create
136  * a configuration control structure.  This is used in the user-level
137  * version of the driver, and in the user-level program that configures
138  * the system via ioctl.
139  */
140 int
141 rf_MakeConfig(char *configname, RF_Config_t *cfgPtr)
142 {
143 	int numscanned, val, c, retcode, aa, bb, cc;
144 	char buf[BUFSIZ], buf1[BUFSIZ], *cp;
145 	const RF_LayoutSW_t *lp;
146 	FILE *fp;
147 
148 	memset(cfgPtr, 0, sizeof(*cfgPtr));
149 
150 	fp = fopen(configname, "r");
151 	if (!fp) {
152 		warnx("Can't open config file %s", configname);
153 		return -1;
154 	}
155 	rewind(fp);
156 	if (rf_search_file_for_start_of("array", buf, sizeof(buf), fp)) {
157 		warnx("Unable to find start of \"array\" params in config "
158 		    "file %s", configname);
159 		retcode = -1;
160 		goto out;
161 	}
162 	rf_get_next_nonblank_line(buf, sizeof(buf), fp,
163 	    "Config file error (\"array\" section):  unable to get numRow "
164 	    "and numCol");
165 
166 	/*
167 	 * wackiness with aa, bb, cc to get around size problems on
168 	 * different platforms
169 	 */
170 
171 	/*
172 	 * Allow both "numCol numSpare" as well as old-style
173 	 * "numRow numCol numSpare".
174 	 * Note that numRow has always been ignored.
175 	 */
176 	numscanned = sscanf(buf, "%d %d %d", &aa, &bb, &cc);
177 	if (numscanned != 3) {
178 		numscanned = sscanf(buf, "%d %d", &bb, &cc);
179 		if (numscanned != 2) {
180 			warnx("Config file error (\"array\" section): unable "
181 			    "to get numCol, numSpare");
182 			retcode = -1;
183 			goto out;
184 		}
185 	}
186 	cfgPtr->numCol = (RF_RowCol_t) bb;
187 	cfgPtr->numSpare = (RF_RowCol_t) cc;
188 
189 	/* debug section is optional */
190 	for (c = 0; c < RF_MAXDBGV; c++)
191 		cfgPtr->debugVars[c][0] = '\0';
192 	rewind(fp);
193 	if (!rf_search_file_for_start_of("debug", buf, sizeof(buf), fp)) {
194 		for (c = 0; c < RF_MAXDBGV; c++) {
195 			if (rf_get_next_nonblank_line(buf, sizeof(buf), fp,
196 			    NULL))
197 				break;
198 			cp = rf_find_non_white(buf, 0);
199 			if (!strncmp(cp, "START", sizeof("START") - 1))
200 				break;
201 			(void) strlcpy(cfgPtr->debugVars[c], cp,
202 			    sizeof(cfgPtr->debugVars[c]));
203 		}
204 	}
205 	rewind(fp);
206 	strlcpy(cfgPtr->diskQueueType, "fifo", sizeof(cfgPtr->diskQueueType));
207 	cfgPtr->maxOutstandingDiskReqs = 1;
208 
209 	/* scan the file for the block related to disk queues */
210 	if (rf_search_file_for_start_of("queue", buf, sizeof(buf), fp) ||
211 	    rf_get_next_nonblank_line(buf, sizeof(buf), fp, NULL)) {
212 		warnx("[No disk queue discipline specified in config file %s. "
213 		    "Using %s.]", configname, cfgPtr->diskQueueType);
214 	}
215 
216 	/*
217 	 * the queue specifier line contains two entries: 1st char of first
218 	 * word specifies queue to be used 2nd word specifies max num reqs
219 	 * that can be outstanding on the disk itself (typically 1)
220 	 */
221 	if (sscanf(buf, "%s %d", buf1, &val) != 2) {
222 		warnx("Can't determine queue type and/or max outstanding "
223 		    "reqs from line: %*s", (int)(sizeof(buf) - 1), buf);
224 		warnx("Using %s-%d", cfgPtr->diskQueueType,
225 		    cfgPtr->maxOutstandingDiskReqs);
226 	} else {
227 		char *ch;
228 		memcpy(cfgPtr->diskQueueType, buf1,
229 		    RF_MIN(sizeof(cfgPtr->diskQueueType), strlen(buf1) + 1));
230 		for (ch = buf1; *ch; ch++) {
231 			if (*ch == ' ') {
232 				*ch = '\0';
233 				break;
234 			}
235 		}
236 		cfgPtr->maxOutstandingDiskReqs = val;
237 	}
238 
239 	rewind(fp);
240 
241 	if (rf_search_file_for_start_of("disks", buf, sizeof(buf), fp)) {
242 		warnx("Can't find \"disks\" section in config file %s",
243 		    configname);
244 		retcode = -1;
245 		goto out;
246 	}
247 	for (c = 0; c < cfgPtr->numCol; c++) {
248 		char b1[MAXPATHLEN];
249 		const char *b;
250 
251 		if (rf_get_next_nonblank_line(
252 		    buf, sizeof(buf), fp, NULL)) {
253 			warnx("Config file error: unable to get device "
254 			    "file for disk at row %d col %d", 0, c);
255 			retcode = -1;
256 			goto out;
257 		}
258 
259 		b = getfsspecname(b1, sizeof(b1), buf);
260 		if (b == NULL) {
261 			warnx("Config file error: warning: unable to "
262 			    "get device file for disk at row %d col "
263 			    "%d: %s", 0, c, b1);
264 			b = buf;
265 		}
266 
267 		strlcpy(cfgPtr->devnames[0][c], b,
268 		    sizeof(cfgPtr->devnames[0][c]));
269 	}
270 
271 	/* "spare" section is optional */
272 	rewind(fp);
273 	if (rf_search_file_for_start_of("spare", buf, sizeof(buf), fp))
274 		cfgPtr->numSpare = 0;
275 	for (c = 0; c < cfgPtr->numSpare; c++) {
276 		char b1[MAXPATHLEN];
277 		const char *b;
278 
279 		if (rf_get_next_nonblank_line(buf, sizeof(buf), fp, NULL)) {
280 			warnx("Config file error: unable to get device file "
281 			    "for spare disk %d", c);
282 			retcode = -1;
283 			goto out;
284 		}
285 
286 		b = getfsspecname(b1, sizeof(b1), buf);
287 		if (b == NULL) {
288 			warnx("Config file error: warning: unable to get "
289 			    "device file for spare disk %d: %s", c, b);
290 			b = buf;
291 		}
292 
293 	        strlcpy(cfgPtr->spare_names[c], b,
294 		    sizeof(cfgPtr->spare_names[c]));
295 	}
296 
297 	/* scan the file for the block related to layout */
298 	rewind(fp);
299 	if (rf_search_file_for_start_of("layout", buf, sizeof(buf), fp)) {
300 		warnx("Can't find \"layout\" section in configuration file %s",
301 		    configname);
302 		retcode = -1;
303 		goto out;
304 	}
305 	if (rf_get_next_nonblank_line(buf, sizeof(buf), fp, NULL)) {
306 		warnx("Config file error (\"layout\" section): unable to find "
307 		    "common layout param line");
308 		retcode = -1;
309 		goto out;
310 	}
311 	c = sscanf(buf, "%d %d %d %c", &aa, &bb, &cc, &cfgPtr->parityConfig);
312 	cfgPtr->sectPerSU = (RF_SectorNum_t) aa;
313 	cfgPtr->SUsPerPU = (RF_StripeNum_t) bb;
314 	cfgPtr->SUsPerRU = (RF_StripeNum_t) cc;
315 	if (c != 4) {
316 		warnx("Unable to scan common layout line");
317 		retcode = -1;
318 		goto out;
319 	}
320 	lp = rf_GetLayout(cfgPtr->parityConfig);
321 	if (lp == NULL) {
322 		warnx("Unknown parity config '%c'",
323 		    cfgPtr->parityConfig);
324 		retcode = -1;
325 		goto out;
326 	}
327 
328 	retcode = lp->MakeLayoutSpecific(fp, cfgPtr,
329 	    lp->makeLayoutSpecificArg);
330 out:
331 	fclose(fp);
332 	if (retcode < 0)
333 		retcode = errno = EINVAL;
334 	else
335 		errno = retcode;
336 	return retcode;
337 }
338 
339 
340 /*
341  * used in architectures such as RAID0 where there is no layout-specific
342  * information to be passed into the configuration code.
343  */
344 int
345 rf_MakeLayoutSpecificNULL(FILE *fp, RF_Config_t *cfgPtr, void *ignored)
346 {
347 	cfgPtr->layoutSpecificSize = 0;
348 	cfgPtr->layoutSpecific = NULL;
349 	return 0;
350 }
351 
352 int
353 rf_MakeLayoutSpecificDeclustered(FILE *configfp, RF_Config_t *cfgPtr, void *arg)
354 {
355 	int b, v, k, r, lambda, norotate, i, val, distSpare;
356 	char *cfgBuf, *bdfile, *p, *smname;
357 	char buf[BUFSIZ], smbuf[BUFSIZ];
358 	FILE *fp;
359 
360 	distSpare = *((int *) arg);
361 
362 	/* get the block design file name */
363 	if (rf_get_next_nonblank_line(buf, sizeof(buf), configfp,
364 	    "Can't find block design file name in config file"))
365 		return EINVAL;
366 	bdfile = rf_find_non_white(buf, 1);
367 	/* open bd file, check validity of configuration */
368 	if ((fp = fopen(bdfile, "r")) == NULL) {
369 		warn("RAID: config error: Can't open layout table file %s",
370 		    bdfile);
371 		return EINVAL;
372 	}
373 	if (fgets(buf, sizeof(buf), fp) == NULL) {
374 		warnx("RAID: config error: Can't read layout from layout "
375 		    "table file %s", bdfile);
376 		fclose(fp);
377 		return EINVAL;
378 	}
379 	i = sscanf(buf, "%u %u %u %u %u %u",
380 	    &b, &v, &k, &r, &lambda, &norotate);
381 	if (i == 5)
382 		norotate = 0;	/* no-rotate flag is optional */
383 	else if (i != 6) {
384 		warnx("Unable to parse header line in block design file");
385 		fclose(fp);
386 		return EINVAL;
387 	}
388 	/*
389 	 * set the sparemap directory.  In the in-kernel version, there's a
390 	 * daemon that's responsible for finding the sparemaps
391 	 */
392 	if (distSpare) {
393 		if (rf_get_next_nonblank_line(smbuf, sizeof(smbuf), configfp,
394 		    "Can't find sparemap file name in config file")) {
395 			fclose(fp);
396 			return EINVAL;
397 		}
398 		smname = rf_find_non_white(smbuf, 1);
399 		if (strlen(smname) >= RF_SPAREMAP_NAME_LEN) {
400 			warnx("sparemap file name '%s' too long (max %d)",
401 			    smname, RF_SPAREMAP_NAME_LEN - 1);
402 			fclose(fp);
403 			return EINVAL;
404 		}
405 	} else {
406 		smbuf[0] = '\0';
407 		smname = smbuf;
408 	}
409 
410 	/* allocate a buffer to hold the configuration info */
411 	cfgPtr->layoutSpecificSize = RF_SPAREMAP_NAME_LEN +
412 	    6 * sizeof(int) + b * k;
413 
414 	cfgBuf = (char *) malloc(cfgPtr->layoutSpecificSize);
415 	if (cfgBuf == NULL) {
416 		fclose(fp);
417 		return ENOMEM;
418 	}
419 	cfgPtr->layoutSpecific = (void *) cfgBuf;
420 	p = cfgBuf;
421 
422 	/* install name of sparemap file */
423 	for (i = 0; smname[i]; i++)
424 		*p++ = smname[i];
425 	/* pad with zeros */
426 	while (i < RF_SPAREMAP_NAME_LEN) {
427 		*p++ = '\0';
428 		i++;
429 	}
430 	if ((i & (sizeof(int) - 1)) != 0) {
431 		/* panic, unaligned data; RF_SPAREMAP_NAME_LEN invalid */
432 		warnx("Program Bug: (RF_SPAREMAP_NAME_LEN(%d) %% %zd) != 0",
433 		    RF_SPAREMAP_NAME_LEN, sizeof(int));
434 		fclose(fp);
435 		return EINVAL;
436 	}
437 
438 	/*
439 	 * fill in the buffer with the block design parameters
440 	 * and then the block design itself
441 	 */
442 	*((int *) p) = b;
443 	p += sizeof(int);
444 	*((int *) p) = v;
445 	p += sizeof(int);
446 	*((int *) p) = k;
447 	p += sizeof(int);
448 	*((int *) p) = r;
449 	p += sizeof(int);
450 	*((int *) p) = lambda;
451 	p += sizeof(int);
452 	*((int *) p) = norotate;
453 	p += sizeof(int);
454 
455 	while (fscanf(fp, "%d", &val) == 1)
456 		*p++ = (char) val;
457 	fclose(fp);
458 	if ((unsigned int)(p - cfgBuf) != cfgPtr->layoutSpecificSize) {
459 		warnx("Size mismatch creating layout specific data: is %tu sb "
460 		    "%zu bytes", p - cfgBuf, 6 * sizeof(int) + b * k);
461 		return EINVAL;
462 	}
463 	return 0;
464 }
465 
466 /****************************************************************************
467  *
468  * utilities
469  *
470  ***************************************************************************/
471 
472 /* finds a non-white character in the line */
473 static char *
474 rf_find_non_white(char *p, int eatnl)
475 {
476 	while (*p == ' ' || *p == '\t')
477 		p++;
478 	if (*p == '\n' && eatnl)
479 		*p = '\0';
480 	return p;
481 }
482 
483 /* finds a white character in the line */
484 static char *
485 rf_find_white(char *p)
486 {
487 	while (*p != '\0' && *p != ' ' && *p != '\t')
488 		p++;
489 	return p;
490 }
491 
492 /*
493  * searches a file for a line that says "START string", where string is
494  * specified as a parameter
495  */
496 static int
497 rf_search_file_for_start_of(const char *string, char *buf, int len, FILE *fp)
498 {
499 	char *p;
500 
501 	while (1) {
502 		if (fgets(buf, len, fp) == NULL)
503 			return -1;
504 		p = rf_find_non_white(buf, 0);
505 		if (!strncmp(p, "START", strlen("START"))) {
506 			p = rf_find_white(p);
507 			p = rf_find_non_white(p, 0);
508 			if (!strncmp(p, string, strlen(string)))
509 				return 0;
510 		}
511 	}
512 }
513 
514 /* reads from file fp into buf until it finds an interesting line */
515 static int
516 rf_get_next_nonblank_line(char *buf, int len, FILE *fp, const char *errmsg)
517 {
518 	char *p;
519 	size_t l;
520 
521 	while (fgets(buf, len, fp) != NULL) {
522 		p = rf_find_non_white(buf, 0);
523 		if (*p == '\n' || *p == '\0' || *p == '#')
524 			continue;
525 		l = strlen(buf);
526 		while (l > 0 && (buf[--l] == ' ' || buf[l] == '\n'))
527 			buf[l] = '\0';
528 		return 0;
529 	}
530 	if (errmsg)
531 		warnx("%s", errmsg);
532 	return 1;
533 }
534 
535 /*
536  * Allocates an array for the spare table, and initializes it from a file.
537  * In the user-level version, this is called when recon is initiated.
538  * When/if I move recon into the kernel, there'll be a daemon that does
539  * an ioctl into raidframe which will block until a spare table is needed.
540  * When it returns, it will read a spare table from the file system,
541  * pass it into the kernel via a different ioctl, and then block again
542  * on the original ioctl.
543  *
544  * This is specific to the declustered layout, but doesn't belong in
545  * rf_decluster.c because it uses stuff that can't be compiled into
546  * the kernel, and it needs to be compiled into the user-level sparemap daemon.
547  */
548 void *
549 rf_ReadSpareTable(RF_SparetWait_t *req, char *fname)
550 {
551 	int i, j, numFound, linecount, tableNum, tupleNum,
552 	    spareDisk, spareBlkOffset;
553 	char buf[BUFSIZ], targString[BUFSIZ], errString[BUFSIZ];
554 	RF_SpareTableEntry_t **table;
555 	FILE *fp = NULL;
556 	size_t len;
557 
558 	/* allocate and initialize the table */
559 	table = calloc(req->TablesPerSpareRegion, sizeof(*table));
560 	if (table == NULL) {
561 		warn("%s: Unable to allocate table", __func__);
562 		return NULL;
563 	}
564 	for (i = 0; i < req->TablesPerSpareRegion; i++) {
565 		table[i] = calloc(req->BlocksPerTable, sizeof(**table));
566 		if (table[i] == NULL) {
567 			warn("%s: Unable to allocate table:%d", __func__, i);
568 			goto out;
569 		}
570 		for (j = 0; j < req->BlocksPerTable; j++)
571 			table[i][j].spareDisk =
572 			    table[i][j].spareBlockOffsetInSUs = -1;
573 	}
574 
575 	/* 2.  open sparemap file, sanity check */
576 	if ((fp = fopen(fname, "r")) == NULL) {
577 		warn("%s: Can't open sparemap file %s", __func__, fname);
578 		goto out;
579 	}
580 	if (rf_get_next_nonblank_line(buf, 1024, fp,
581 	    "Invalid sparemap file:  can't find header line"))
582 		goto out;
583 
584 	len = strlen(buf);
585 	if (len != 0 && buf[len - 1] == '\n')
586 		buf[len - 1] = '\0';
587 
588 	snprintf(targString, sizeof(targString), "fdisk %d\n", req->fcol);
589 	snprintf(errString, sizeof(errString),
590 	    "Invalid sparemap file: Can't find \"fdisk %d\" line", req->fcol);
591 	for (;;) {
592 		rf_get_next_nonblank_line(buf, sizeof(buf), fp, errString);
593 		if (!strncmp(buf, targString, strlen(targString)))
594 			break;
595 	}
596 
597 	/* no more blank lines or comments allowed now */
598 	linecount = req->TablesPerSpareRegion * req->TableDepthInPUs;
599 	for (i = 0; i < linecount; i++) {
600 		char linebuf[BUFSIZ];
601 
602 		if (fgets(linebuf, BUFSIZ, fp) == NULL) {
603 			warnx("Sparemap file prematurely exhausted after %d "
604 			    "of %d lines", i, linecount);
605 			goto out;
606 		}
607 		numFound = sscanf(linebuf, " %d %d %d %d", &tableNum, &tupleNum,
608 		    &spareDisk, &spareBlkOffset);
609 		if (numFound != 4) {
610 			warnx("Sparemap file format error - "
611 			    "line %d of %d lines",
612 			    i + 1, linecount);
613 			goto out;
614 		}
615 
616 		table[tableNum][tupleNum].spareDisk = spareDisk;
617 		table[tableNum][tupleNum].spareBlockOffsetInSUs =
618 		    spareBlkOffset * req->SUsPerPU;
619 	}
620 
621 	fclose(fp);
622 	return (void *) table;
623 out:
624 	if (fp)
625 		fclose(fp);
626 	for (i = 0; i < req->TablesPerSpareRegion; i++)
627 		free(table[i]);
628 	free(table);
629 	return NULL;
630 }
631