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