1 /* $NetBSD: edahdi.c,v 1.1.1.1 1996/05/16 19:53:00 leo Exp $ */ 2 3 /* 4 * Copyright (c) 1996 Leo Weppelman, Waldi Ravens. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. The name of the author may not be used to endorse or promote products 16 * derived from this software without specific prior written permission. 17 * 4. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed by 20 * Leo Weppelman and Waldi Ravens. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 27 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 31 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 /* 35 * This code implements a simple editor for partition id's on disks containing 36 * AHDI partition info. 37 * 38 * Credits for the code handling disklabels goes to Waldi Ravens. 39 * 40 */ 41 #include <sys/types.h> 42 #include <sys/param.h> 43 #include <sys/stat.h> 44 #include <sys/disklabel.h> 45 46 #include <machine/ahdilabel.h> 47 48 #include <fcntl.h> 49 #include <stdlib.h> 50 #include <curses.h> 51 #include <termios.h> 52 #include <unistd.h> 53 #include <stdio.h> 54 #include <string.h> 55 #include <err.h> 56 #include <ctype.h> 57 58 /* 59 * Internal partition tables: 60 */ 61 typedef struct { 62 char id[4]; 63 u_int start; 64 u_int end; 65 u_int rsec; 66 u_int rent; 67 int mod; 68 } part_t; 69 70 typedef struct { 71 int nparts; 72 part_t *parts; 73 } ptable_t; 74 75 /* 76 * I think we can savely assume a fixed blocksize - AHDI won't support 77 * something different... 78 */ 79 #define BLPM ((1024 * 1024) / DEV_BSIZE) 80 81 /* 82 * #Partition entries shown on the screen at once 83 */ 84 #define MAX_PSHOWN 16 /* #partitions shown on screen */ 85 86 /* 87 * Tokens: 88 */ 89 #define T_INVAL 0 90 #define T_QUIT 1 91 #define T_WRITE 2 92 #define T_NEXT 3 93 #define T_PREV 4 94 #define T_NUMBER 5 95 #define T_EOF 6 96 97 /* 98 * Terminal capability strings (Ok, 1 to start with ;-) ) 99 */ 100 char *Clr_screen = ""; 101 102 void ahdi_cksum __P((void *)); 103 u_int ahdi_getparts __P((int, ptable_t *, u_int, u_int)); 104 int bsd_label __P((int, u_int)); 105 int dkcksum __P((struct disklabel *)); 106 int edit_parts __P((int, ptable_t *)); 107 void *disk_read __P((int, u_int, u_int)); 108 void disk_write __P((int, u_int, u_int, void *)); 109 char *get_id __P((void)); 110 void get_termcap __P((void)); 111 int lex __P((int *)); 112 void outc __P((int)); 113 int show_parts __P((ptable_t *, int)); 114 void update_disk __P((ptable_t *, int, int)); 115 116 int 117 main(argc, argv) 118 int argc; 119 char *argv[]; 120 { 121 int fd; 122 ptable_t ptable; 123 int rv; 124 struct stat st; 125 126 if (argc != 2) { 127 char *prog = strrchr(argv[0], '/'); 128 129 if (prog == NULL) 130 prog = argv[0]; 131 else prog++; 132 fprintf(stderr, "Usage: %s <raw_disk_device>", prog); 133 exit(1); 134 } 135 if ((fd = open(argv[1], O_RDWR)) < 0) 136 err(1, "Cannot open '%s'.", argv[1]); 137 if (fstat(fd, &st) < 0) 138 err(1, "Cannot stat '%s'.", argv[1]); 139 if (!S_ISCHR(st.st_mode)) 140 errx(1, "'%s' must be a character special device.", argv[1]); 141 142 if ((rv = bsd_label(fd, LABELSECTOR)) < 0) 143 errx(1, "I/O error"); 144 if (rv == 0) { 145 warnx("Disk has no ahdi partitions"); 146 return (2); 147 } 148 149 get_termcap(); 150 151 ptable.nparts = 0; 152 ptable.parts = NULL; 153 154 if ((ahdi_getparts(fd, &ptable, AHDI_BBLOCK, AHDI_BBLOCK) != 0) 155 || (ptable.nparts == 0)) 156 exit (1); 157 158 edit_parts(fd, &ptable); 159 return (0); 160 } 161 162 int 163 edit_parts(fd, ptable) 164 int fd; 165 ptable_t *ptable; 166 { 167 int scr_base = 0; 168 int value; 169 char *error, *new_id; 170 171 for (;;) { 172 error = NULL; 173 tputs(Clr_screen, 1, outc); 174 show_parts(ptable, scr_base); 175 176 printf("\n\n"); 177 printf("q : quit - don't update the disk\n"); 178 printf("w : write changes to disk\n"); 179 printf("> : next screen of partitions\n"); 180 printf("< : previous screen of partitions\n"); 181 printf("<nr> : modify id of partition <nr>\n"); 182 printf("\n\nCommand? "); 183 fflush(stdout); 184 185 switch (lex(&value)) { 186 case T_EOF: 187 exit(0); 188 189 case T_INVAL: 190 error = "Invalid command"; 191 break; 192 case T_QUIT : 193 for (value = 0; value < ptable->nparts; value++) { 194 if (ptable->parts[value].mod) { 195 printf("\nThere are unwritten changes." 196 " Quit anyway? [n] "); 197 value = getchar(); 198 if ((value == 'y') || (value == 'Y')) 199 exit (0); 200 while (value != '\n') 201 value = getchar(); 202 break; 203 } 204 } 205 if (value == ptable->nparts) 206 exit(0); 207 break; 208 case T_WRITE: 209 error = "No changes to write"; 210 for (value = 0; value < ptable->nparts; value++) { 211 if (ptable->parts[value].mod) { 212 update_disk(ptable, fd, value); 213 error = ""; 214 } 215 } 216 break; 217 case T_NEXT : 218 if ((scr_base + MAX_PSHOWN) < ptable->nparts) 219 scr_base += MAX_PSHOWN; 220 break; 221 case T_PREV : 222 scr_base -= MAX_PSHOWN; 223 if (scr_base < 0) 224 scr_base = 0; 225 break; 226 case T_NUMBER: 227 if (value >= ptable->nparts) { 228 error = "Not that many partitions"; 229 break; 230 } 231 if ((new_id = get_id()) == NULL) { 232 error = "Invalid id"; 233 break; 234 } 235 strncpy(ptable->parts[value].id, new_id, 3); 236 ptable->parts[value].mod = 1; 237 scr_base = (value / MAX_PSHOWN) * MAX_PSHOWN; 238 break; 239 default : 240 error = "Internal error - unknown token"; 241 break; 242 } 243 if (error != NULL) { 244 printf("\n\n%s", error); 245 fflush(stdout); 246 sleep(2); 247 } 248 } 249 } 250 251 int 252 show_parts(ptable, nr) 253 ptable_t *ptable; 254 int nr; 255 { 256 int i; 257 part_t *p; 258 u_int megs; 259 260 if (nr >= ptable->nparts) 261 return (0); /* Nothing to show */ 262 printf("\n\n"); 263 printf("nr root desc id start end MBs\n"); 264 265 p = &ptable->parts[nr]; 266 i = nr; 267 for(; (i < ptable->nparts) && ((i - nr) < MAX_PSHOWN); i++, p++) { 268 megs = ((p->end - p->start + 1) + (BLPM >> 1)) / BLPM; 269 printf("%2d%s %8u %4u %s %8u %8u (%3u)\n", i, 270 p->mod ? "*" : " ", 271 p->rsec, p->rent, p->id, p->start, p->end, megs); 272 } 273 return (1); 274 } 275 276 int 277 lex(value) 278 int *value; 279 { 280 char c[1]; 281 int rv, nch; 282 283 rv = T_INVAL; 284 285 *value = 0; 286 for (;;) { 287 if ((nch = read (0, c, 1)) != 1) { 288 if (nch == 0) 289 return (T_EOF); 290 else return (rv); 291 } 292 switch (*c) { 293 case 'q': 294 rv = T_QUIT; 295 goto out; 296 case 'w': 297 rv = T_WRITE; 298 goto out; 299 case '>': 300 rv = T_NEXT; 301 goto out; 302 case '<': 303 rv = T_PREV; 304 goto out; 305 default : 306 if (isspace(*c)) { 307 if (rv == T_INVAL) 308 break; 309 goto out; 310 } 311 else if (isdigit(*c)) { 312 *value = (10 * *value) + *c - '0'; 313 rv = T_NUMBER; 314 } 315 goto out; 316 } 317 } 318 /* NOTREACHED */ 319 out: 320 /* 321 * Flush rest of line before returning 322 */ 323 while (read (0, c, 1) == 1) 324 if ((*c == '\n') || (*c == '\r')) 325 break; 326 return (rv); 327 } 328 329 char * 330 get_id() 331 { 332 static char buf[5]; 333 int n; 334 printf("\nEnter new id: "); 335 if (fgets(buf, sizeof(buf), stdin) == NULL) 336 return (NULL); 337 for (n = 0; n < 3; n++) { 338 if (!isalpha(buf[n])) 339 return (NULL); 340 buf[n] = toupper(buf[n]); 341 } 342 buf[3] = '\0'; 343 return (buf); 344 } 345 346 int 347 bsd_label(fd, offset) 348 int fd; 349 u_int offset; 350 { 351 u_char *bblk; 352 u_int nsec; 353 int rv; 354 355 nsec = (BBMINSIZE + (DEV_BSIZE - 1)) / DEV_BSIZE; 356 bblk = disk_read(fd, offset, nsec); 357 if (bblk) { 358 u_int *end, *p; 359 360 end = (u_int *)&bblk[BBMINSIZE - sizeof(struct disklabel)]; 361 rv = 1; 362 for (p = (u_int *)bblk; p < end; ++p) { 363 struct disklabel *dl = (struct disklabel *)&p[1]; 364 if ( ( (p[0] == NBDAMAGIC && offset == 0) 365 || (p[0] == AHDIMAGIC && offset != 0) 366 || (u_char *)dl - bblk == 7168 367 ) 368 && dl->d_npartitions <= MAXPARTITIONS 369 && dl->d_magic2 == DISKMAGIC 370 && dl->d_magic == DISKMAGIC 371 && dkcksum(dl) == 0 372 ) { 373 rv = 0; 374 break; 375 } 376 } 377 free(bblk); 378 } 379 else rv = -1; 380 381 return(rv); 382 } 383 384 int 385 dkcksum(dl) 386 struct disklabel *dl; 387 { 388 u_short *start, *end, sum = 0; 389 390 start = (u_short *)dl; 391 end = (u_short *)&dl->d_partitions[dl->d_npartitions]; 392 while (start < end) 393 sum ^= *start++; 394 return(sum); 395 } 396 397 void 398 ahdi_cksum(buf) 399 void *buf; 400 { 401 unsigned short *p = (unsigned short *)buf; 402 unsigned short csum = 0; 403 int i; 404 405 p[255] = 0; 406 for(i = 0; i < 256; i++) 407 csum += *p++; 408 *--p = (0x1234 - csum) & 0xffff; 409 } 410 411 412 u_int 413 ahdi_getparts(fd, ptable, rsec, esec) 414 int fd; 415 ptable_t *ptable; 416 u_int rsec, 417 esec; 418 { 419 struct ahdi_part *part, *end; 420 struct ahdi_root *root; 421 u_int rv; 422 423 root = disk_read(fd, rsec, 1); 424 if (!root) { 425 rv = rsec + (rsec == 0); 426 goto done; 427 } 428 429 if (rsec == AHDI_BBLOCK) 430 end = &root->ar_parts[AHDI_MAXRPD]; 431 else end = &root->ar_parts[AHDI_MAXARPD]; 432 for (part = root->ar_parts; part < end; ++part) { 433 u_int id = *((u_int32_t *)&part->ap_flg); 434 if (!(id & 0x01000000)) 435 continue; 436 if ((id &= 0x00ffffff) == AHDI_PID_XGM) { 437 u_int offs = part->ap_st + esec; 438 rv = ahdi_getparts(fd, ptable, offs, 439 esec == AHDI_BBLOCK ? offs : esec); 440 if (rv) 441 goto done; 442 } else { 443 part_t *p; 444 u_int i = ++ptable->nparts; 445 ptable->parts = realloc(ptable->parts, 446 i * sizeof *ptable->parts); 447 if (ptable->parts == NULL) { 448 fprintf(stderr, "Allocation error\n"); 449 rv = 1; 450 goto done; 451 } 452 p = &ptable->parts[--i]; 453 *((u_int32_t *)&p->id) = id << 8; 454 p->start = part->ap_st + rsec; 455 p->end = p->start + part->ap_size - 1; 456 p->rsec = rsec; 457 p->rent = part - root->ar_parts; 458 p->mod = 0; 459 } 460 } 461 rv = 0; 462 done: 463 free(root); 464 return(rv); 465 } 466 467 void * 468 disk_read(fd, start, count) 469 int fd; 470 u_int start, 471 count; 472 { 473 char *buffer; 474 off_t offset; 475 size_t size; 476 477 size = count * DEV_BSIZE; 478 offset = start * DEV_BSIZE; 479 if ((buffer = malloc(size)) == NULL) 480 errx(1, "No memory"); 481 482 if (lseek(fd, offset, SEEK_SET) != offset) { 483 free(buffer); 484 err(1, "Seek error"); 485 } 486 if (read(fd, buffer, size) != size) { 487 free(buffer); 488 err(1, "Read error"); 489 exit(1); 490 } 491 return(buffer); 492 } 493 494 void 495 update_disk(ptable, fd, pno) 496 ptable_t *ptable; 497 int fd, pno; 498 { 499 struct ahdi_root *root; 500 struct ahdi_part *apart; 501 part_t *lpart; 502 u_int rsec; 503 int i; 504 505 rsec = ptable->parts[pno].rsec; 506 root = disk_read(fd, rsec, 1); 507 508 /* 509 * Look for additional mods on the same sector 510 */ 511 for (i = 0; i < ptable->nparts; i++) { 512 lpart = &ptable->parts[i]; 513 if (lpart->mod && (lpart->rsec == rsec)) { 514 apart = &root->ar_parts[lpart->rent]; 515 516 /* Paranoia.... */ 517 if ((lpart->end - lpart->start + 1) != apart->ap_size) 518 errx(1, "Updating wrong partition!"); 519 apart->ap_id[0] = lpart->id[0]; 520 apart->ap_id[1] = lpart->id[1]; 521 apart->ap_id[2] = lpart->id[2]; 522 lpart->mod = 0; 523 } 524 } 525 if (rsec == 0) 526 ahdi_cksum(root); 527 disk_write(fd, rsec, 1, root); 528 free(root); 529 } 530 531 void 532 disk_write(fd, start, count, buf) 533 int fd; 534 u_int start, 535 count; 536 void *buf; 537 { 538 off_t offset; 539 size_t size; 540 541 size = count * DEV_BSIZE; 542 offset = start * DEV_BSIZE; 543 544 if (lseek(fd, offset, SEEK_SET) != offset) 545 err(1, "Seek error"); 546 if (write(fd, buf, size) != size) 547 err(1, "Write error"); 548 } 549 550 void 551 get_termcap() 552 { 553 char *term, tbuf[1024], buf[1024], *p; 554 555 if ((term = getenv("TERM")) == NULL) 556 warnx("No TERM environment variable!"); 557 else { 558 if (tgetent(tbuf, term) != 1) 559 errx(1, "Tgetent failure."); 560 p = buf; 561 if (tgetstr("cl", &p)) { 562 if ((Clr_screen = malloc(strlen(buf) + 1)) == NULL) 563 errx(1, "Malloc failure."); 564 strcpy(Clr_screen, buf); 565 } 566 } 567 } 568 569 void 570 outc(c) 571 int c; 572 { 573 (void)putchar(c); 574 } 575