1 /* $NetBSD: storage.c,v 1.13 2009/06/23 05:08:22 agc Exp $ */
4 * Copyright (c) 2009 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Alistair Crooks (agc@netbsd.org)
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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.
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.
33 #include <sys/types.h>
36 #ifdef HAVE_INTTYPES_H
55 #include "iscsiprotocol.h"
56 #include "iscsiutil.h"
63 /* let's use symbolic names for the fields in the config file */
66 EXTENT_DEVICE_COL
= 1,
67 EXTENT_SACRED_COL
= 2,
68 EXTENT_LENGTH_COL
= 3,
71 DEVICE_RAIDLEVEL_COL
= 1,
72 DEVICE_LENGTH_COL
= 2,
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
82 #define DEFAULT_FLAGS "ro"
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))
89 /* find an extent by name */
90 static disc_extent_t
*
91 find_extent(extv_t
*extents
, char *s
)
95 for (i
= 0 ; i
< extents
->c
; i
++) {
96 if (strcmp(extents
->v
[i
].extent
, s
) == 0) {
97 return &extents
->v
[i
];
103 /* allocate space for a new extent */
105 do_extent(conffile_t
*cf
, extv_t
*extents
, ent_t
*ep
)
107 disc_extent_t
*extent
;
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
]);
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
],
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
;
131 extent
->len
= strtoll(ep
->sv
.v
[EXTENT_LENGTH_COL
], &cp
, 10);
133 switch(tolower((unsigned)*cp
)) {
135 extent
->len
= TERABYTES(extent
->len
);
138 extent
->len
= GIGABYTES(extent
->len
);
141 extent
->len
= MEGABYTES(extent
->len
);
144 extent
->len
= KILOBYTES(extent
->len
);
152 /* find a device by name */
153 static disc_device_t
*
154 find_device(devv_t
*devvp
, char *s
)
158 for (i
= 0 ; i
< devvp
->c
; i
++) {
159 if (strcmp(devvp
->v
[i
].dev
, s
) == 0) {
166 /* return the size of the sub-device/extent */
168 getsize(conffile_t
*cf
, devv_t
*devvp
, extv_t
*extents
, char *s
)
173 if ((xp
= find_extent(extents
, s
)) != NULL
) {
176 if ((dp
= find_device(devvp
, s
)) != NULL
) {
177 switch (dp
->xv
[0].type
) {
179 return dp
->xv
[0].u
.xp
->len
;
181 return dp
->xv
[0].u
.dp
->len
;
184 (void) fprintf(stderr
, "%s:%d: Warning: no sub-device/extent `%s'\n",
185 conffile_get_name(cf
),
186 conffile_get_lineno(cf
),
191 /* allocate space for a device */
193 do_device(conffile_t
*cf
, devv_t
*devvp
, extv_t
*extents
, ent_t
*ep
)
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
),
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
);
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",
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
,
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]);
232 if (disk
->xv
[disk
->c
].u
.xp
->len
!= disk
->len
&&
234 (void) fprintf(stderr
,
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
,
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
,
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]);
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
;
264 /* not an extent or device */
265 (void) fprintf(stderr
,
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]);
274 if (disk
->raid
== 1) {
275 /* check we have more than 1 device/extent */
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
),
290 /* find a target by name */
291 static disc_target_t
*
292 find_target(targv_t
*targs
, char *s
)
296 for (i
= 0 ; i
< targs
->c
; i
++) {
297 if (strcmp(targs
->v
[i
].target
, s
) == 0) {
304 /* allocate space for a new target */
306 do_target(conffile_t
*cf
, targv_t
*targs
, devv_t
*devvp
, extv_t
*extents
, ent_t
*ep
)
316 if ((iqn
= strchr(ep
->sv
.v
[TARGET_NAME_COL
], '=')) == NULL
) {
317 (void) strlcpy(tgt
, ep
->sv
.v
[TARGET_NAME_COL
], sizeof(tgt
));
319 (void) snprintf(tgt
, sizeof(tgt
), "%.*s",
320 (int)(iqn
- ep
->sv
.v
[TARGET_NAME_COL
]),
321 ep
->sv
.v
[TARGET_NAME_COL
]);
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
),
332 ALLOC(disc_target_t
, targs
->v
, targs
->size
, targs
->c
, 14, 14,
333 "do_target", exit(EXIT_FAILURE
));
335 /* 3 columns in entry - old style declaration */
336 (void) fprintf(stderr
,
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
;
346 devcol
= TARGET_V2_DEVICE_COL
;
347 flags
= ep
->sv
.v
[TARGET_V2_FLAGS_COL
];
348 netmaskcol
= TARGET_V2_NETMASK_COL
;
351 targs
->v
[targs
->c
].iqn
= strdup(iqn
);
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
;
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
;
379 (void) fprintf(stderr
,
381 "Error: no device or extent found for `%s'\n",
382 conffile_get_name(cf
),
383 conffile_get_lineno(cf
),
388 /* print an extent */
390 pextent(disc_extent_t
*ep
, int indent
)
394 for (i
= 0 ; i
< indent
; i
++) {
395 (void) fputc('\t', stdout
);
397 printf("%s:%s:%" PRIu64
":%" PRIu64
"\n", ep
->extent
, ep
->dev
,
398 ep
->sacred
, ep
->len
);
401 static void pdevice(disc_device_t
*, int);
403 /* print information about an extent or a device */
405 pu(disc_de_t
*dep
, int indent
)
409 pextent(dep
->u
.xp
, indent
);
412 pdevice(dep
->u
.dp
, indent
);
417 /* print information about a device */
419 pdevice(disc_device_t
*dp
, int indent
)
424 for (i
= 0 ; i
< indent
; i
++) {
425 (void) fputc('\t', stdout
);
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);
433 /* print informnation about a target */
435 ptarget(disc_target_t
*tp
, int indent
)
439 for (i
= 0 ; i
< indent
; i
++) {
440 (void) fputc('\t', stdout
);
442 printf("%s:%s:%s\n", tp
->target
,
443 (tp
->flags
& TARGET_READONLY
) ? "ro" : "rw", tp
->mask
);
444 pu(&tp
->de
, indent
+ 1);
447 /* print all information */
449 ptargets(targv_t
*targs
)
453 for (i
= 0 ; i
< targs
->c
; i
++) {
454 ptarget(&targs
->v
[i
], 0);
458 /* read a configuration file */
460 read_conf_file(const char *cf
, targv_t
*targs
, devv_t
*devs
, extv_t
*extents
)
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
);
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
);
484 (void) conffile_close(&conf
);