xref: /netbsd-src/external/bsd/iscsi/dist/src/lib/storage.c (revision 5e01dafb5c1f3a68ee2f80136c3b6043ad89a0f3)
1 /* $NetBSD: storage.c,v 1.2 2009/06/30 02:44:52 agc Exp $ */
2 
3 /*-
4  * Copyright (c) 2009 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Alistair Crooks (agc@netbsd.org)
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 #include "config.h"
32 
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 
36 #ifdef HAVE_INTTYPES_H
37 #include <inttypes.h>
38 #endif
39 
40 #ifdef HAVE_SIGNAL_H
41 #include <signal.h>
42 #endif
43 
44 #include <ctype.h>
45 #include <errno.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 
49 #ifdef HAVE_STRING_H
50 #include <string.h>
51 #endif
52 
53 #include <unistd.h>
54 
55 #include "iscsiprotocol.h"
56 #include "iscsiutil.h"
57 #include "target.h"
58 #include "device.h"
59 
60 #include "conffile.h"
61 #include "storage.h"
62 
63 /* let's use symbolic names for the fields in the config file */
64 enum {
65 	EXTENT_NAME_COL = 0,
66 	EXTENT_DEVICE_COL = 1,
67 	EXTENT_SACRED_COL = 2,
68 	EXTENT_LENGTH_COL = 3,
69 
70 	DEVICE_NAME_COL = 0,
71 	DEVICE_RAIDLEVEL_COL = 1,
72 	DEVICE_LENGTH_COL = 2,
73 
74 	TARGET_NAME_COL = 0,
75 	TARGET_V1_DEVICE_COL = 1,
76 	TARGET_V1_NETMASK_COL = 2,
77 	TARGET_V2_FLAGS_COL = 1,
78 	TARGET_V2_DEVICE_COL = 2,
79 	TARGET_V2_NETMASK_COL = 3
80 };
81 
82 #define DEFAULT_FLAGS	"ro"
83 
84 #define TERABYTES(x)	(uint64_t)(1024ULL * 1024ULL * 1024ULL * 1024ULL * (x))
85 #define GIGABYTES(x)	(uint64_t)(1024ULL * 1024ULL * 1024ULL * (x))
86 #define MEGABYTES(x)	(uint64_t)(1024ULL * 1024ULL * (x))
87 #define KILOBYTES(x)	(uint64_t)(1024ULL * (x))
88 
89 /* find an extent by name */
90 static disc_extent_t *
find_extent(extv_t * extents,char * s)91 find_extent(extv_t *extents, char *s)
92 {
93 	size_t	i;
94 
95 	for (i = 0 ; i < extents->c ; i++) {
96 		if (strcmp(extents->v[i].extent, s) == 0) {
97 			return &extents->v[i];
98 		}
99 	}
100 	return NULL;
101 }
102 
103 /* allocate space for a new extent */
104 static int
do_extent(conffile_t * cf,extv_t * extents,ent_t * ep)105 do_extent(conffile_t *cf, extv_t *extents, ent_t *ep)
106 {
107 	disc_extent_t	*extent;
108 	struct stat	 st;
109 	char		*cp;
110 
111 	if (find_extent(extents, ep->sv.v[EXTENT_NAME_COL]) != NULL) {
112 		(void) fprintf(stderr,
113 			"%s:%d: Error: attempt to re-define extent `%s'\n",
114 			conffile_get_name(cf),
115 			conffile_get_lineno(cf),
116 			ep->sv.v[EXTENT_NAME_COL]);
117 		return 0;
118 	}
119 	ALLOC(disc_extent_t, extents->v, extents->size, extents->c, 14, 14,
120 			"do_extent", exit(EXIT_FAILURE));
121 	extent = &extents->v[extents->c++];
122 	extent->extent = strdup(ep->sv.v[EXTENT_NAME_COL]);
123 	extent->dev = strdup(ep->sv.v[EXTENT_DEVICE_COL]);
124 	extent->sacred = strtoll(ep->sv.v[EXTENT_SACRED_COL],
125 			NULL, 10);
126 	if (strcasecmp(ep->sv.v[EXTENT_LENGTH_COL], "size") == 0) {
127 		if (stat(ep->sv.v[EXTENT_DEVICE_COL], &st) == 0) {
128 			extent->len = st.st_size;
129 		}
130 	} else {
131 		extent->len = strtoll(ep->sv.v[EXTENT_LENGTH_COL], &cp, 10);
132 		if (cp != NULL) {
133 			switch(tolower((unsigned)*cp)) {
134 			case 't':
135 				extent->len = TERABYTES(extent->len);
136 				break;
137 			case 'g':
138 				extent->len = GIGABYTES(extent->len);
139 				break;
140 			case 'm':
141 				extent->len = MEGABYTES(extent->len);
142 				break;
143 			case 'k':
144 				extent->len = KILOBYTES(extent->len);
145 				break;
146 			}
147 		}
148 	}
149 	return 1;
150 }
151 
152 /* find a device by name */
153 static disc_device_t *
find_device(devv_t * devvp,char * s)154 find_device(devv_t *devvp, char *s)
155 {
156 	size_t	i;
157 
158 	for (i = 0 ; i < devvp->c ; i++) {
159 		if (strcmp(devvp->v[i].dev, s) == 0) {
160 			return &devvp->v[i];
161 		}
162 	}
163 	return NULL;
164 }
165 
166 /* return the size of the sub-device/extent */
167 static uint64_t
getsize(conffile_t * cf,devv_t * devvp,extv_t * extents,char * s)168 getsize(conffile_t *cf, devv_t *devvp, extv_t *extents, char *s)
169 {
170 	disc_extent_t	*xp;
171 	disc_device_t	*dp;
172 
173 	if ((xp = find_extent(extents, s)) != NULL) {
174 		return xp->len;
175 	}
176 	if ((dp = find_device(devvp, s)) != NULL) {
177 		switch (dp->xv[0].type) {
178 		case DE_EXTENT:
179 			return dp->xv[0].u.xp->len;
180 		case DE_DEVICE:
181 			return dp->xv[0].u.dp->len;
182 		}
183 	}
184 	(void) fprintf(stderr, "%s:%d: Warning: no sub-device/extent `%s'\n",
185 				conffile_get_name(cf),
186 				conffile_get_lineno(cf),
187 				s);
188 	return 0;
189 }
190 
191 /* allocate space for a device */
192 static int
do_device(conffile_t * cf,devv_t * devvp,extv_t * extents,ent_t * ep)193 do_device(conffile_t *cf, devv_t *devvp, extv_t *extents, ent_t *ep)
194 {
195 	disc_device_t	*disk;
196 	char		*device;
197 
198 	device = ep->sv.v[DEVICE_NAME_COL];
199 	if ((disk = find_device(devvp, device)) != NULL) {
200 		(void) fprintf(stderr,
201 			"%s:%d: Error: attempt to re-define device `%s'\n",
202 			conffile_get_name(cf),
203 			conffile_get_lineno(cf),
204 			device);
205 		return 0;
206 	}
207 	ALLOC(disc_device_t, devvp->v, devvp->size, devvp->c, 14, 14,
208 				"do_device", exit(EXIT_FAILURE));
209 	disk = &devvp->v[devvp->c];
210 	disk->dev = strdup(device);
211 	disk->raid =
212 		(strncasecmp(ep->sv.v[DEVICE_RAIDLEVEL_COL], "raid", 4) == 0) ?
213 		atoi(&ep->sv.v[DEVICE_RAIDLEVEL_COL][4]) : 0;
214 	disk->size = ep->sv.c - 2;
215 	disk->len = getsize(cf, devvp, extents, ep->sv.v[DEVICE_LENGTH_COL]);
216 	NEWARRAY(disc_de_t, disk->xv, ep->sv.c - 2, "do_device",
217 			exit(EXIT_FAILURE));
218 	for (disk->c = 0 ; disk->c < disk->size ; disk->c++) {
219 		disk->xv[disk->c].u.xp =
220 				find_extent(extents, ep->sv.v[disk->c + 2]);
221 		if (disk->xv[disk->c].u.xp != NULL) {
222 			/* a reference to an extent */
223 			if (disk->xv[disk->c].u.xp->used) {
224 				(void) fprintf(stderr,
225 					"%s:%d: "
226 				"Error: extent `%s' has already been used\n",
227 					conffile_get_name(cf),
228 					conffile_get_lineno(cf),
229 					ep->sv.v[disk->c + 2]);
230 				return 0;
231 			}
232 			if (disk->xv[disk->c].u.xp->len != disk->len &&
233 							disk->raid != 0) {
234 				(void) fprintf(stderr,
235 					"%s:%d: "
236 					"Error: extent `%s' has size %" PRIu64
237 					", not %" PRIu64"\n",
238 					conffile_get_name(cf),
239 					conffile_get_lineno(cf),
240 					ep->sv.v[disk->c + 2],
241 					disk->xv[disk->c].u.xp->len,
242 					disk->len);
243 				return 0;
244 			}
245 			disk->xv[disk->c].type = DE_EXTENT;
246 			disk->xv[disk->c].size = disk->xv[disk->c].u.xp->len;
247 			disk->xv[disk->c].u.xp->used = 1;
248 		} else if ((disk->xv[disk->c].u.dp =
249 			find_device(devvp, ep->sv.v[disk->c + 2])) != NULL) {
250 			/* a reference to a device */
251 			if (disk->xv[disk->c].u.dp->used) {
252 				(void) fprintf(stderr,
253 					"%s:%d: "
254 				"Error: device `%s' has already been used\n",
255 					conffile_get_name(cf),
256 					conffile_get_lineno(cf),
257 					ep->sv.v[disk->c + 2]);
258 				return 0;
259 			}
260 			disk->xv[disk->c].type = DE_DEVICE;
261 			disk->xv[disk->c].u.dp->used = 1;
262 			disk->xv[disk->c].size = disk->xv[disk->c].u.dp->len;
263 		} else {
264 			/* not an extent or device */
265 			(void) fprintf(stderr,
266 				"%s:%d: "
267 				"Error: no extent or device found for `%s'\n",
268 				conffile_get_name(cf),
269 				conffile_get_lineno(cf),
270 				ep->sv.v[disk->c + 2]);
271 			return 0;
272 		}
273 	}
274 	if (disk->raid == 1) {
275 		/* check we have more than 1 device/extent */
276 		if (disk->c < 2) {
277 			(void) fprintf(stderr,
278 					"%s:%d: Error: device `%s' is RAID1, "
279 					"but has only %d sub-devices/extents\n",
280 					conffile_get_name(cf),
281 					conffile_get_lineno(cf),
282 					disk->dev, disk->c);
283 			return 0;
284 		}
285 	}
286 	devvp->c += 1;
287 	return 1;
288 }
289 
290 /* find a target by name */
291 static disc_target_t *
find_target(targv_t * targs,char * s)292 find_target(targv_t *targs, char *s)
293 {
294 	size_t	i;
295 
296 	for (i = 0 ; i < targs->c ; i++) {
297 		if (strcmp(targs->v[i].target, s) == 0) {
298 			return &targs->v[i];
299 		}
300 	}
301 	return NULL;
302 }
303 
304 /* allocate space for a new target */
305 static int
do_target(conffile_t * cf,targv_t * targs,devv_t * devvp,extv_t * extents,ent_t * ep)306 do_target(conffile_t *cf, targv_t *targs, devv_t *devvp, extv_t *extents, ent_t *ep)
307 {
308 	disc_extent_t	*xp;
309 	disc_device_t	*dp;
310 	const char	*flags;
311 	char		 tgt[256];
312 	char		*iqn;
313 	int		 netmaskcol;
314 	int		 devcol;
315 
316 	if ((iqn = strchr(ep->sv.v[TARGET_NAME_COL], '=')) == NULL) {
317 		(void) strlcpy(tgt, ep->sv.v[TARGET_NAME_COL], sizeof(tgt));
318 	} else {
319 		(void) snprintf(tgt, sizeof(tgt), "%.*s",
320 				(int)(iqn - ep->sv.v[TARGET_NAME_COL]),
321 				ep->sv.v[TARGET_NAME_COL]);
322 		iqn += 1;
323 	}
324 	if (find_target(targs, tgt) != NULL) {
325 		(void) fprintf(stderr,
326 			"%s:%d: Error: attempt to re-define target `%s'\n",
327 			conffile_get_name(cf),
328 			conffile_get_lineno(cf),
329 			tgt);
330 		return 0;
331 	}
332 	ALLOC(disc_target_t, targs->v, targs->size, targs->c, 14, 14,
333 			"do_target", exit(EXIT_FAILURE));
334 	if (ep->sv.c == 3) {
335 		/* 3 columns in entry - old style declaration */
336 		(void) fprintf(stderr,
337 				"%s:%d: "
338 				"Warning: old 3 field \"targets\" entry"
339 				"assuming read-only target\n",
340 				conffile_get_name(cf),
341 				conffile_get_lineno(cf));
342 		devcol = TARGET_V1_DEVICE_COL;
343 		netmaskcol = TARGET_V1_NETMASK_COL;
344 		flags = DEFAULT_FLAGS;
345 	} else {
346 		devcol = TARGET_V2_DEVICE_COL;
347 		flags = ep->sv.v[TARGET_V2_FLAGS_COL];
348 		netmaskcol = TARGET_V2_NETMASK_COL;
349 	}
350 	if (iqn != NULL) {
351 		targs->v[targs->c].iqn = strdup(iqn);
352 	}
353 	if ((dp = find_device(devvp, ep->sv.v[devcol])) != NULL) {
354 		/* we have a device */
355 		targs->v[targs->c].de.type = DE_DEVICE;
356 		targs->v[targs->c].de.u.dp = dp;
357 		targs->v[targs->c].target = strdup(tgt);
358 		targs->v[targs->c].mask = strdup(ep->sv.v[netmaskcol]);
359 		if (strcmp(flags, "readonly") == 0 ||
360 		    strcmp(flags, "ro") == 0 || strcmp(flags, "r") == 0) {
361 			targs->v[targs->c].flags |= TARGET_READONLY;
362 		}
363 		targs->c += 1;
364 		return 1;
365 	}
366 	if ((xp = find_extent(extents, ep->sv.v[devcol])) != NULL) {
367 		/* we have an extent */
368 		targs->v[targs->c].de.type = DE_EXTENT;
369 		targs->v[targs->c].de.u.xp = xp;
370 		targs->v[targs->c].target = strdup(tgt);
371 		targs->v[targs->c].mask = strdup(ep->sv.v[netmaskcol]);
372 		if (strcmp(flags, "readonly") == 0 ||
373 		    strcmp(flags, "ro") == 0 || strcmp(flags, "r") == 0) {
374 			targs->v[targs->c].flags |= TARGET_READONLY;
375 		}
376 		targs->c += 1;
377 		return 1;
378 	}
379 	(void) fprintf(stderr,
380 			"%s:%d: "
381 			"Error: no device or extent found for `%s'\n",
382 			conffile_get_name(cf),
383 			conffile_get_lineno(cf),
384 			ep->sv.v[devcol]);
385 	return 0;
386 }
387 
388 /* print an extent */
389 static void
pextent(disc_extent_t * ep,int indent)390 pextent(disc_extent_t *ep, int indent)
391 {
392 	int	i;
393 
394 	for (i = 0 ; i < indent ; i++) {
395 		(void) fputc('\t', stdout);
396 	}
397 	printf("%s:%s:%" PRIu64 ":%" PRIu64 "\n", ep->extent, ep->dev,
398 			ep->sacred, ep->len);
399 }
400 
401 static void pdevice(disc_device_t *, int);
402 
403 /* print information about an extent or a device */
404 static void
pu(disc_de_t * dep,int indent)405 pu(disc_de_t *dep, int indent)
406 {
407 	switch(dep->type) {
408 	case DE_EXTENT:
409 		pextent(dep->u.xp, indent);
410 		break;
411 	case DE_DEVICE:
412 		pdevice(dep->u.dp, indent);
413 		break;
414 	}
415 }
416 
417 /* print information about a device */
418 static void
pdevice(disc_device_t * dp,int indent)419 pdevice(disc_device_t *dp, int indent)
420 {
421 	size_t	j;
422 	int	i;
423 
424 	for (i = 0 ; i < indent ; i++) {
425 		(void) fputc('\t', stdout);
426 	}
427 	printf("%s:RAID%d\n", dp->dev, dp->raid);
428 	for (j = 0 ; j < dp->c ; j++) {
429 		pu(&dp->xv[j], indent + 1);
430 	}
431 }
432 
433 /* print informnation about a target */
434 static void
ptarget(disc_target_t * tp,int indent)435 ptarget(disc_target_t *tp, int indent)
436 {
437 	int	i;
438 
439 	for (i = 0 ; i < indent ; i++) {
440 		(void) fputc('\t', stdout);
441 	}
442 	printf("%s:%s:%s\n", tp->target,
443 		(tp->flags & TARGET_READONLY) ? "ro" : "rw", tp->mask);
444 	pu(&tp->de, indent + 1);
445 }
446 
447 /* print all information */
448 static void
ptargets(targv_t * targs)449 ptargets(targv_t *targs)
450 {
451 	size_t	i;
452 
453 	for (i = 0 ; i < targs->c ; i++) {
454 		ptarget(&targs->v[i], 0);
455 	}
456 }
457 
458 /* read a configuration file */
459 int
read_conf_file(const char * cf,targv_t * targs,devv_t * devs,extv_t * extents)460 read_conf_file(const char *cf, targv_t *targs, devv_t *devs, extv_t *extents)
461 {
462 	conffile_t	conf;
463 	ent_t		e;
464 
465 	(void) memset(&conf, 0x0, sizeof(conf));
466 	if (!conffile_open(&conf, cf, "r", " \t", "#")) {
467 		(void) fprintf(stderr, "Error: can't open `%s'\n", cf);
468 		return 0;
469 	}
470 	printf("Reading configuration from `%s'\n", cf);
471 	(void) memset(&e, 0x0, sizeof(e));
472 	while (conffile_getent(&conf, &e)) {
473 		if (strncmp(e.sv.v[0], "extent", 6) == 0) {
474 			do_extent(&conf, extents, &e);
475 		} else if (strncmp(e.sv.v[0], "device", 6) == 0) {
476 			do_device(&conf, devs, extents, &e);
477 		} else if (strncmp(e.sv.v[0], "target", 6) == 0 ||
478 			   strncmp(e.sv.v[0], "lun", 3) == 0) {
479 			do_target(&conf, targs, devs, extents, &e);
480 		}
481 		e.sv.c = 0;
482 	}
483 	ptargets(targs);
484 	(void) conffile_close(&conf);
485 	return 1;
486 }
487 
488