1/* 2 * ioconf: ioconf configuration file handling code 3 * Original code (C) 2004 by Red Hat (Charlie Bennett <ccb@redhat.com>) 4 * 5 * Modified and maintained by Sebastien GODARD (sysstat <at> orange.fr) 6 * 7 *************************************************************************** 8 * This program is free software; you can redistribute it and/or modify it * 9 * under the terms of the GNU General Public License as published by the * 10 * Free Software Foundation; either version 2 of the License, or (at your * 11 * option) any later version. * 12 * * 13 * This program is distributed in the hope that it will be useful, but * 14 * WITHOUT ANY WARRANTY; without the implied warranty of MERCHANTABILITY * 15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * 16 * for more details. * 17 * * 18 * You should have received a copy of the GNU General Public License along * 19 * with this program; if not, write to the Free Software Foundation, Inc., * 20 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * 21 *************************************************************************** 22 */ 23 24#include <stdio.h> 25#include <stdlib.h> 26#include <string.h> 27#include <errno.h> 28#include <dirent.h> 29#include <sys/stat.h> 30 31#include "ioconf.h" 32#include "common.h" 33 34#ifdef USE_NLS 35#include <locale.h> 36#include <libintl.h> 37#define _(string) gettext(string) 38#else 39#define _(string) (string) 40#endif 41 42static unsigned int ioc_parsed = 0; 43static struct ioc_entry *ioconf[MAX_BLKDEV + 1]; 44static unsigned int ioc_refnr[MAX_BLKDEV + 1]; 45 46/* 47 *************************************************************************** 48 * Free ioc_entry structures 49 *************************************************************************** 50 */ 51static void ioc_free(void) 52{ 53 unsigned int i; 54 struct ioc_entry **p; 55 56 /* Take out all of the references first */ 57 for (i = 0, p = ioconf; i < MAX_BLKDEV; ++i, ++p) { 58 if ((*p == NULL) || ((*p)->live)) 59 continue; 60 61 if ((*p)->desc != (*p)->blkp->desc) { 62 /* Not a shared description */ 63 free((*p)->desc); 64 } 65 free(*p); 66 *p = NULL; 67 } 68 69 /* Now the live ones */ 70 for (i = 0, p = ioconf; i < MAX_BLKDEV; ++i, ++p) { 71 if (*p == NULL) 72 continue; 73 free((*p)->blkp); 74 free(*p); 75 *p = NULL; 76 } 77} 78 79/* 80 *************************************************************************** 81 * ioc_conv - Turn a number into a string in radix <radix> using symbol 82 * set (and ordering) syms. Use nozero to generate strings 83 * in which the number system uses a single sym for the 84 * radix value (not 2, like decimal) and adds a column only 85 * at radix+1. If decimal were like this: 86 * 87 * (no zero) 1 2 3 4 5 6 7 8 9 0 11 12 13 14 15 16 17 18 19 10 ... 88 *************************************************************************** 89 */ 90static char *ioc_conv(int radix, int nozero, const char *syms, 91 unsigned int val) 92{ 93 static char out[17]; 94 char *p; 95 int j; 96 97 *(p = out + 16) = '\0'; 98 99 val += nozero; 100 101 if (val == 0) { 102 if (!nozero) { 103 *--p = '0'; 104 } 105 return (p); /* Empty string if nozero radix gets val == 0 */ 106 } 107 108 while (val > 0) { 109 *--p = syms[j = val % radix]; 110 val /= radix; 111 if (nozero && (j == 0)) { 112 /* Comp for 10 in nozero bases */ 113 --val; 114 } 115 } 116 return (p); 117} 118 119char *ioc_ito10(unsigned int n) 120{ 121 return (ioc_conv(10, 0, "0123456789", n)); 122} 123 124char *ioc_ito26(unsigned int n) 125{ 126 return (ioc_conv(26, 1, "zabcdefghijklmnopqrstuvwxy", n)); 127} 128 129/* 130 *************************************************************************** 131 * ioc_init() - internalize the ioconf file 132 * 133 * given: void 134 * does: parses IOCONF into ioconf, an array of ioc_entry * 135 * Only entries having lines in IOCONF will have valid pointers 136 * return: 1 on success 137 * 0 on failure 138 *************************************************************************** 139 */ 140int ioc_init(void) 141{ 142 FILE *fp; 143 unsigned int i, major, indirect, count = 0; 144 char buf[IOC_LINESIZ + 1]; 145 char cfmt[IOC_FMTLEN + 1]; 146 char dfmt[IOC_FMTLEN + 1]; 147 char pfmt[IOC_FMTLEN + 1]; 148 char desc[IOC_DESCLEN + 1]; 149 struct ioc_entry *iocp = NULL; 150 struct blk_config *blkp = NULL; 151 char ioconf_name[64]; 152 153 if ((fp = fopen(IOCONF, "r")) == NULL) { 154 if ((fp = fopen(LOCAL_IOCONF, "r")) == NULL) 155 return 0; 156 strncpy(ioconf_name, LOCAL_IOCONF, 64); 157 } 158 else { 159 strncpy(ioconf_name, IOCONF, 64); 160 } 161 ioconf_name[63] = '\0'; 162 163 /* Init ioc_refnr array */ 164 memset(ioc_refnr, 0, sizeof(ioc_refnr)); 165 166 while (fgets(buf, IOC_LINESIZ, fp)) { 167 168 if ((*buf == '#') || (*buf == '\n')) 169 continue; 170 171 /* 172 * Preallocate some (probably) needed data structures 173 */ 174 IOC_ALLOC(blkp, struct blk_config, BLK_CONFIG_SIZE); 175 IOC_ALLOC(iocp, struct ioc_entry, IOC_ENTRY_SIZE); 176 memset(blkp, 0, BLK_CONFIG_SIZE); 177 memset(iocp, 0, IOC_ENTRY_SIZE); 178 179 i = sscanf(buf, "%u:%u:%u:%s", 180 &major, &indirect, &iocp->ctrlno, desc); 181 182 if (i != 4) { 183 i = sscanf(buf, "%u:%u:%u", 184 &major, &indirect, &iocp->ctrlno); 185 } 186 187 if ((i == 3) || (i == 4)) { 188 /* indirect record */ 189 if (indirect == 0) { 190 /* conventional usage for unsupported device */ 191 continue; 192 } 193 if (indirect >= MAX_BLKDEV) { 194 fprintf(stderr, "%s: Indirect major #%u out of range\n", 195 ioconf_name, indirect); 196 continue; 197 } 198 if (ioconf[indirect] == NULL) { 199 fprintf(stderr, 200 "%s: Indirect record '%u:%u:%u:...'" 201 " references not yet seen major %u\n", 202 ioconf_name, major, indirect, iocp->ctrlno, major); 203 continue; 204 } 205 /* 206 * Cool. Point this device at its referent. 207 * Skip last: (last field my be empty...) 208 * if it was empty and : was in the sscanf spec 209 * we'd only see 3 fields... 210 */ 211 if (i == 3) { 212 /* reference the mothership */ 213 iocp->desc = ioconf[indirect]->blkp->desc; 214 } 215 else { 216 IOC_ALLOC(iocp->desc, char, IOC_DESCLEN + 1); 217 strncpy(iocp->desc, desc, IOC_DESCLEN); 218 } 219 ioc_refnr[indirect]++; 220 ioconf[major] = iocp; 221 iocp->basemajor = indirect; 222 iocp->blkp = ioconf[indirect]->blkp; 223 iocp->live = 0; 224 iocp = NULL; 225 continue; 226 /* all done with indirect record */ 227 } 228 229 /* maybe it's a full record? */ 230 231 i = sscanf(buf, "%u:%[^:]:%[^:]:%d:%[^:]:%u:%[^:]:%u:%s", 232 &major, blkp->name, 233 cfmt, &iocp->ctrlno, 234 dfmt, &blkp->dcount, 235 pfmt, &blkp->pcount, 236 desc); 237 238 if (i != 9) { 239 fprintf(stderr, "%s: Malformed %d field record: %s\n", 240 ioconf_name, i, buf); 241 continue; 242 } 243 244 /* this is a full-fledged direct record */ 245 246 if ((major == 0) || (major >= MAX_BLKDEV)) { 247 fprintf(stderr, "%s: major #%u out of range\n", 248 __FUNCTION__, major); 249 continue; 250 } 251 252 /* is this an exception record? */ 253 if (*cfmt == 'x') { 254 struct blk_config *xblkp; 255 256 /* 257 * device has an aliased minor 258 * for now we only support on exception per major 259 * (catering to initrd: (1,250)) 260 */ 261 if (ioconf[major] == NULL) { 262 fprintf(stderr, "%s: type 'x' record for" 263 " major #%u must follow the base record - ignored\n", 264 ioconf_name, major); 265 continue; 266 } 267 xblkp = ioconf[major]->blkp; 268 269 if (xblkp->ext) { 270 /* 271 * Enforce one minor exception per major policy 272 * note: this applies to each major number and 273 * all of it's indirect (short form) majors 274 */ 275 fprintf(stderr, "%s: duplicate 'x' record for" 276 " major #%u - ignored\ninput line: %s\n", 277 ioconf_name, major, buf); 278 continue; 279 } 280 /* 281 * Decorate the base major struct with the 282 * exception info 283 */ 284 xblkp->ext_minor = iocp->ctrlno; 285 strcpy(xblkp->ext_name, blkp->name); 286 xblkp->ext = 1; 287 continue; 288 } 289 290 /* 291 * Preformat the sprintf format strings for generating 292 * c-d-p info in ioc_name() 293 */ 294 295 /* basename of device + provided string + controller # */ 296 if (*cfmt == '*') { 297 strcpy(blkp->cfmt, blkp->name); 298 } 299 else { 300 sprintf(blkp->cfmt, "%s%s%%d", blkp->name, cfmt); 301 ++(blkp->ctrl_explicit); 302 } 303 304 /* Disk */ 305 *blkp->dfmt = '\0'; 306 switch (*dfmt) { 307 case 'a': 308 blkp->cconv = ioc_ito26; 309 strcpy(blkp->dfmt, "%s"); 310 break; 311 312 case '%': 313 strcpy(blkp->dfmt, dfmt + 1); 314 case 'd': 315 blkp->cconv = ioc_ito10; 316 strcat(blkp->dfmt, "%s"); 317 break; 318 } 319 320 /* Partition */ 321 sprintf(blkp->pfmt, "%s%%d", (*pfmt == '*') ? "" : pfmt); 322 323 /* 324 * We're good to go. 325 * Stuff the ioc_entry and ref it. 326 */ 327 iocp->live = 1; 328 iocp->blkp = blkp; 329 iocp->desc = NULL; 330 iocp->basemajor = major; 331 ioconf[major] = iocp; 332 strncpy(blkp->desc, desc, IOC_DESCLEN); 333 blkp = NULL; iocp = NULL; 334 ++count; 335 } 336 fclose(fp); 337 338 /* 339 * These will become leaks if we ever 'continue' 340 * after IOC_ALLOC( blkp->desc ... ). 341 * Right now, we don't. 342 */ 343 if (blkp != NULL) 344 free(blkp); 345 if (iocp != NULL) 346 free(iocp); 347 348 /* Indicate that ioconf file has been parsed */ 349 ioc_parsed = 1; 350 351 return (count); 352} 353 354/* 355 *************************************************************************** 356 * ioc_name() - Generate a name from a maj,min pair 357 * 358 * IN: 359 * @major Device major number. 360 * @minor Device minor number. 361 * 362 * RETURNS: 363 * Returns NULL if major or minor are out of range 364 * otherwise returns a pointer to a static string containing 365 * the generated name. 366 *************************************************************************** 367 */ 368 369char *ioc_name(unsigned int major, unsigned int minor) 370{ 371 static char name[IOC_DEVLEN + 1]; 372 struct ioc_entry *p; 373 int base, offset; 374 375 if ((MAX_BLKDEV <= major) || (IOC_MAXMINOR <= minor)) { 376 return (NULL); 377 } 378 379 if (!ioc_parsed && !ioc_init()) 380 return (NULL); 381 382 p = ioconf[major]; 383 384 /* Invalid major or minor numbers? */ 385 if ((p == NULL) || ((minor & 0xff) >= (p->blkp->dcount * p->blkp->pcount))) { 386 /* 387 * That minor test is only there for IDE-style devices 388 * that have no minors over 128. 389 */ 390 strcpy(name, K_NODEV); 391 return (name); 392 } 393 394 /* Is this an extension record? */ 395 if (p->blkp->ext && (p->blkp->ext_minor == minor)) { 396 strcpy(name, p->blkp->ext_name); 397 return (name); 398 } 399 400 /* OK. we're doing an actual device name... */ 401 402 /* 403 * Assemble base + optional controller info 404 * this is of course too clever by half 405 * the parser has already cooked cfmt, dfmt to make this easy 406 * (we parse once but may generate lots of names) 407 */ 408 base = p->ctrlno * p->blkp->dcount; 409 if (minor >= 256) { 410 base += p->blkp->dcount * (ioc_refnr[p->basemajor] + 1) * (minor >> 8); 411 } 412 413 offset = (minor & 0xff) / p->blkp->pcount; 414 if (!p->blkp->ctrl_explicit) { 415 offset += base; 416 } 417 418 /* 419 * These sprintfs can't be coalesced because the first might 420 * ignore its first arg 421 */ 422 sprintf(name, p->blkp->cfmt, p->ctrlno); 423 sprintf(name + strlen(name), p->blkp->dfmt, p->blkp->cconv(offset)); 424 425 if (!IS_WHOLE(major, minor)) { 426 /* 427 * Tack on partition info, format string cooked (curried?) by 428 * the parser 429 */ 430 sprintf(name + strlen(name), p->blkp->pfmt, minor % p->blkp->pcount); 431 } 432 return (name); 433} 434 435/* 436 *************************************************************************** 437 * Check whether a device is a whole disk device or not. 438 * 439 * IN: 440 * @major Device major number. 441 * @minor Device minor number. 442 * 443 * RETURNS: 444 * Predicate: Returns 1 if dev (major,minor) is a whole disk device. 445 * Returns 0 otherwise. 446 *************************************************************************** 447 */ 448int ioc_iswhole(unsigned int major, unsigned int minor) 449{ 450 if (!ioc_parsed && !ioc_init()) 451 return 0; 452 453 if (major >= MAX_BLKDEV) 454 /* 455 * Later: Handle Linux long major numbers here. 456 * Now: This is an error. 457 */ 458 return 0; 459 460 if (ioconf[major] == NULL) 461 /* Device not registered */ 462 return 0 ; 463 464 return (IS_WHOLE(major, minor)); 465} 466 467/* 468 *************************************************************************** 469 * Transform device mapper name: Get the user assigned name of the logical 470 * device instead of the internal device mapper numbering. 471 * 472 * IN: 473 * @major Device major number. 474 * @minor Device minor number. 475 * 476 * RETURNS: 477 * Assigned name of the logical device. 478 *************************************************************************** 479 */ 480char *transform_devmapname(unsigned int major, unsigned int minor) 481{ 482 DIR *dm_dir; 483 struct dirent *dp; 484 char filen[MAX_FILE_LEN]; 485 char *dm_name = NULL; 486 static char name[MAX_NAME_LEN]; 487 struct stat aux; 488 unsigned int dm_major, dm_minor; 489 490 if ((dm_dir = opendir(DEVMAP_DIR)) == NULL) { 491 fprintf(stderr, _("Cannot open %s: %s\n"), DEVMAP_DIR, strerror(errno)); 492 exit(4); 493 } 494 495 while ((dp = readdir(dm_dir)) != NULL) { 496 /* For each file in DEVMAP_DIR */ 497 498 snprintf(filen, MAX_FILE_LEN, "%s/%s", DEVMAP_DIR, dp->d_name); 499 filen[MAX_FILE_LEN - 1] = '\0'; 500 501 if (stat(filen, &aux) == 0) { 502 /* Get its minor and major numbers */ 503 504 dm_major = major(aux.st_rdev); 505 dm_minor = minor(aux.st_rdev); 506 507 if ((dm_minor == minor) && (dm_major == major)) { 508 strncpy(name, dp->d_name, MAX_NAME_LEN); 509 name[MAX_NAME_LEN - 1] = '\0'; 510 dm_name = name; 511 break; 512 } 513 } 514 } 515 closedir(dm_dir); 516 517 return dm_name; 518} 519