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