edahdi.c revision 1.7 1 /* $NetBSD: edahdi.c,v 1.7 2009/03/14 15:36:03 dsl 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. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by
18 * Leo Weppelman and Waldi Ravens.
19 * 4. The name of the author may not be used to endorse or promote products
20 * derived from this software without specific prior written permission.
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(void *);
103 u_int ahdi_getparts(int, ptable_t *, u_int, u_int);
104 int bsd_label(int, u_int);
105 int dkcksum(struct disklabel *);
106 int edit_parts(int, ptable_t *);
107 void *disk_read(int, u_int, u_int);
108 void disk_write(int, u_int, u_int, void *);
109 char *get_id(void);
110 void get_termcap(void);
111 int lex(int *);
112 int show_parts(ptable_t *, int);
113 void update_disk(ptable_t *, int, int);
114
115 int
116 main(argc, argv)
117 int argc;
118 char *argv[];
119 {
120 int fd;
121 ptable_t ptable;
122 int rv;
123 struct stat st;
124
125 if (argc != 2) {
126 char *prog = strrchr(argv[0], '/');
127
128 if (prog == NULL)
129 prog = argv[0];
130 else prog++;
131 fprintf(stderr, "Usage: %s <raw_disk_device>", prog);
132 exit(1);
133 }
134 if ((fd = open(argv[1], O_RDWR)) < 0)
135 err(1, "Cannot open '%s'.", argv[1]);
136 if (fstat(fd, &st) < 0)
137 err(1, "Cannot stat '%s'.", argv[1]);
138 if (!S_ISCHR(st.st_mode))
139 errx(1, "'%s' must be a character special device.", argv[1]);
140
141 if ((rv = bsd_label(fd, LABELSECTOR)) < 0)
142 errx(1, "I/O error");
143 if (rv == 0) {
144 warnx("Disk has no ahdi partitions");
145 return (2);
146 }
147
148 get_termcap();
149
150 ptable.nparts = 0;
151 ptable.parts = NULL;
152
153 if ((ahdi_getparts(fd, &ptable, AHDI_BBLOCK, AHDI_BBLOCK) != 0)
154 || (ptable.nparts == 0))
155 exit (1);
156
157 edit_parts(fd, &ptable);
158 return (0);
159 }
160
161 int
162 edit_parts(int fd, ptable_t *ptable)
163 {
164 int scr_base = 0;
165 int value;
166 char *error, *new_id;
167
168 for (;;) {
169 error = NULL;
170 tputs(Clr_screen, 1, putchar);
171 show_parts(ptable, scr_base);
172
173 printf("\n\n");
174 printf("q : quit - don't update the disk\n");
175 printf("w : write changes to disk\n");
176 printf("> : next screen of partitions\n");
177 printf("< : previous screen of partitions\n");
178 printf("<nr> : modify id of partition <nr>\n");
179 printf("\n\nCommand? ");
180 fflush(stdout);
181
182 switch (lex(&value)) {
183 case T_EOF:
184 exit(0);
185
186 case T_INVAL:
187 error = "Invalid command";
188 break;
189 case T_QUIT :
190 for (value = 0; value < ptable->nparts; value++) {
191 if (ptable->parts[value].mod) {
192 printf("\nThere are unwritten changes."
193 " Quit anyway? [n] ");
194 value = getchar();
195 if ((value == 'y') || (value == 'Y'))
196 exit (0);
197 while (value != '\n')
198 value = getchar();
199 break;
200 }
201 }
202 if (value == ptable->nparts)
203 exit(0);
204 break;
205 case T_WRITE:
206 error = "No changes to write";
207 for (value = 0; value < ptable->nparts; value++) {
208 if (ptable->parts[value].mod) {
209 update_disk(ptable, fd, value);
210 error = "";
211 }
212 }
213 break;
214 case T_NEXT :
215 if ((scr_base + MAX_PSHOWN) < ptable->nparts)
216 scr_base += MAX_PSHOWN;
217 break;
218 case T_PREV :
219 scr_base -= MAX_PSHOWN;
220 if (scr_base < 0)
221 scr_base = 0;
222 break;
223 case T_NUMBER:
224 if (value >= ptable->nparts) {
225 error = "Not that many partitions";
226 break;
227 }
228 if ((new_id = get_id()) == NULL) {
229 error = "Invalid id";
230 break;
231 }
232 strncpy(ptable->parts[value].id, new_id, 3);
233 ptable->parts[value].mod = 1;
234 scr_base = (value / MAX_PSHOWN) * MAX_PSHOWN;
235 break;
236 default :
237 error = "Internal error - unknown token";
238 break;
239 }
240 if (error != NULL) {
241 printf("\n\n%s", error);
242 fflush(stdout);
243 sleep(2);
244 }
245 }
246 }
247
248 int
249 show_parts(ptable_t *ptable, int nr)
250 {
251 int i;
252 part_t *p;
253 u_int megs;
254
255 if (nr >= ptable->nparts)
256 return (0); /* Nothing to show */
257 printf("\n\n");
258 printf("nr root desc id start end MBs\n");
259
260 p = &ptable->parts[nr];
261 i = nr;
262 for(; (i < ptable->nparts) && ((i - nr) < MAX_PSHOWN); i++, p++) {
263 megs = ((p->end - p->start + 1) + (BLPM >> 1)) / BLPM;
264 printf("%2d%s %8u %4u %s %8u %8u (%3u)\n", i,
265 p->mod ? "*" : " ",
266 p->rsec, p->rent, p->id, p->start, p->end, megs);
267 }
268 return (1);
269 }
270
271 int
272 lex(int *value)
273 {
274 char c[1];
275 int rv, nch;
276
277 rv = T_INVAL;
278
279 *value = 0;
280 for (;;) {
281 if ((nch = read (0, c, 1)) != 1) {
282 if (nch == 0)
283 return (T_EOF);
284 else return (rv);
285 }
286 switch (*c) {
287 case 'q':
288 rv = T_QUIT;
289 goto out;
290 case 'w':
291 rv = T_WRITE;
292 goto out;
293 case '>':
294 rv = T_NEXT;
295 goto out;
296 case '<':
297 rv = T_PREV;
298 goto out;
299 default :
300 if (isspace((unsigned char)*c)) {
301 if (rv == T_INVAL)
302 break;
303 goto out;
304 }
305 else if (isdigit((unsigned char)*c)) {
306 *value = (10 * *value) + *c - '0';
307 rv = T_NUMBER;
308 }
309 goto out;
310 }
311 }
312 /* NOTREACHED */
313 out:
314 /*
315 * Flush rest of line before returning
316 */
317 while (read (0, c, 1) == 1)
318 if ((*c == '\n') || (*c == '\r'))
319 break;
320 return (rv);
321 }
322
323 char *
324 get_id()
325 {
326 static char buf[5];
327 int n;
328 printf("\nEnter new id: ");
329 if (fgets(buf, sizeof(buf), stdin) == NULL)
330 return (NULL);
331 for (n = 0; n < 3; n++) {
332 if (!isalpha((unsigned char)buf[n]))
333 return (NULL);
334 buf[n] = toupper((unsigned char)buf[n]);
335 }
336 buf[3] = '\0';
337 return (buf);
338 }
339
340 int
341 bsd_label(int fd, u_int offset)
342 {
343 u_char *bblk;
344 u_int nsec;
345 int rv;
346
347 nsec = (BBMINSIZE + (DEV_BSIZE - 1)) / DEV_BSIZE;
348 bblk = disk_read(fd, offset, nsec);
349 if (bblk) {
350 u_int *end, *p;
351
352 end = (u_int *)&bblk[BBMINSIZE - sizeof(struct disklabel)];
353 rv = 1;
354 for (p = (u_int *)bblk; p < end; ++p) {
355 struct disklabel *dl = (struct disklabel *)&p[1];
356 if ( ( (p[0] == NBDAMAGIC && offset == 0)
357 || (p[0] == AHDIMAGIC && offset != 0)
358 || (u_char *)dl - bblk == 7168
359 )
360 && dl->d_npartitions <= MAXPARTITIONS
361 && dl->d_magic2 == DISKMAGIC
362 && dl->d_magic == DISKMAGIC
363 && dkcksum(dl) == 0
364 ) {
365 rv = 0;
366 break;
367 }
368 }
369 free(bblk);
370 }
371 else rv = -1;
372
373 return(rv);
374 }
375
376 int
377 dkcksum(struct disklabel *dl)
378 {
379 u_short *start, *end, sum = 0;
380
381 start = (u_short *)dl;
382 end = (u_short *)&dl->d_partitions[dl->d_npartitions];
383 while (start < end)
384 sum ^= *start++;
385 return(sum);
386 }
387
388 void
389 ahdi_cksum(void *buf)
390 {
391 unsigned short *p = (unsigned short *)buf;
392 unsigned short csum = 0;
393 int i;
394
395 p[255] = 0;
396 for(i = 0; i < 256; i++)
397 csum += *p++;
398 *--p = (0x1234 - csum) & 0xffff;
399 }
400
401
402 u_int
403 ahdi_getparts(fd, ptable, rsec, esec)
404 int fd;
405 ptable_t *ptable;
406 u_int rsec,
407 esec;
408 {
409 struct ahdi_part *part, *end;
410 struct ahdi_root *root;
411 u_int rv;
412
413 root = disk_read(fd, rsec, 1);
414 if (!root) {
415 rv = rsec + (rsec == 0);
416 goto done;
417 }
418
419 if (rsec == AHDI_BBLOCK)
420 end = &root->ar_parts[AHDI_MAXRPD];
421 else end = &root->ar_parts[AHDI_MAXARPD];
422 for (part = root->ar_parts; part < end; ++part) {
423 u_int id = *((u_int32_t *)&part->ap_flg);
424 if (!(id & 0x01000000))
425 continue;
426 if ((id &= 0x00ffffff) == AHDI_PID_XGM) {
427 u_int offs = part->ap_st + esec;
428 rv = ahdi_getparts(fd, ptable, offs,
429 esec == AHDI_BBLOCK ? offs : esec);
430 if (rv)
431 goto done;
432 } else {
433 part_t *p;
434 u_int i = ++ptable->nparts;
435 ptable->parts = realloc(ptable->parts,
436 i * sizeof *ptable->parts);
437 if (ptable->parts == NULL) {
438 fprintf(stderr, "Allocation error\n");
439 rv = 1;
440 goto done;
441 }
442 p = &ptable->parts[--i];
443 *((u_int32_t *)&p->id) = id << 8;
444 p->start = part->ap_st + rsec;
445 p->end = p->start + part->ap_size - 1;
446 p->rsec = rsec;
447 p->rent = part - root->ar_parts;
448 p->mod = 0;
449 }
450 }
451 rv = 0;
452 done:
453 free(root);
454 return(rv);
455 }
456
457 void *
458 disk_read(fd, start, count)
459 int fd;
460 u_int start,
461 count;
462 {
463 char *buffer;
464 off_t offset;
465 size_t size;
466
467 size = count * DEV_BSIZE;
468 offset = start * DEV_BSIZE;
469 if ((buffer = malloc(size)) == NULL)
470 errx(1, "No memory");
471
472 if (lseek(fd, offset, SEEK_SET) != offset) {
473 free(buffer);
474 err(1, "Seek error");
475 }
476 if (read(fd, buffer, size) != size) {
477 free(buffer);
478 err(1, "Read error");
479 exit(1);
480 }
481 return(buffer);
482 }
483
484 void
485 update_disk(ptable, fd, pno)
486 ptable_t *ptable;
487 int fd, pno;
488 {
489 struct ahdi_root *root;
490 struct ahdi_part *apart;
491 part_t *lpart;
492 u_int rsec;
493 int i;
494
495 rsec = ptable->parts[pno].rsec;
496 root = disk_read(fd, rsec, 1);
497
498 /*
499 * Look for additional mods on the same sector
500 */
501 for (i = 0; i < ptable->nparts; i++) {
502 lpart = &ptable->parts[i];
503 if (lpart->mod && (lpart->rsec == rsec)) {
504 apart = &root->ar_parts[lpart->rent];
505
506 /* Paranoia.... */
507 if ((lpart->end - lpart->start + 1) != apart->ap_size)
508 errx(1, "Updating wrong partition!");
509 apart->ap_id[0] = lpart->id[0];
510 apart->ap_id[1] = lpart->id[1];
511 apart->ap_id[2] = lpart->id[2];
512 lpart->mod = 0;
513 }
514 }
515 if (rsec == 0)
516 ahdi_cksum(root);
517 disk_write(fd, rsec, 1, root);
518 free(root);
519 }
520
521 void
522 disk_write(fd, start, count, buf)
523 int fd;
524 u_int start,
525 count;
526 void *buf;
527 {
528 off_t offset;
529 size_t size;
530
531 size = count * DEV_BSIZE;
532 offset = start * DEV_BSIZE;
533
534 if (lseek(fd, offset, SEEK_SET) != offset)
535 err(1, "Seek error");
536 if (write(fd, buf, size) != size)
537 err(1, "Write error");
538 }
539
540 void
541 get_termcap()
542 {
543 char *term, tbuf[1024], buf[1024], *p;
544
545 if ((term = getenv("TERM")) == NULL)
546 warnx("No TERM environment variable!");
547 else {
548 if (tgetent(tbuf, term) != 1)
549 errx(1, "Tgetent failure.");
550 p = buf;
551 if (tgetstr("cl", &p)) {
552 if ((Clr_screen = malloc(strlen(buf) + 1)) == NULL)
553 errx(1, "Malloc failure.");
554 strcpy(Clr_screen, buf);
555 }
556 }
557 }
558