/* $NetBSD: edahdi.c,v 1.11 2011/10/01 15:59:00 chs Exp $ */ /* * Copyright (c) 1996 Leo Weppelman, Waldi Ravens. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by * Leo Weppelman and Waldi Ravens. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * This code implements a simple editor for partition id's on disks containing * AHDI partition info. * * Credits for the code handling disklabels goes to Waldi Ravens. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Internal partition tables: */ typedef struct { char id[4]; u_int start; u_int end; u_int rsec; u_int rent; int mod; } part_t; typedef struct { int nparts; part_t *parts; } ptable_t; /* * I think we can savely assume a fixed blocksize - AHDI won't support * something different... */ #define BLPM ((1024 * 1024) / DEV_BSIZE) /* * #Partition entries shown on the screen at once */ #define MAX_PSHOWN 16 /* #partitions shown on screen */ /* * Tokens: */ #define T_INVAL 0 #define T_QUIT 1 #define T_WRITE 2 #define T_NEXT 3 #define T_PREV 4 #define T_NUMBER 5 #define T_EOF 6 void ahdi_cksum(void *); u_int ahdi_getparts(int, ptable_t *, u_int, u_int); int bsd_label(int, u_int); int dkcksum(struct disklabel *); int edit_parts(int, ptable_t *); void *disk_read(int, u_int, u_int); void disk_write(int, u_int, u_int, void *); char *get_id(void); int lex(int *); int show_parts(ptable_t *, int); void update_disk(ptable_t *, int, int); int main(int argc, char *argv[]) { int fd; ptable_t ptable; int rv; struct stat st; if (argc != 2) { char *prog = strrchr(argv[0], '/'); if (prog == NULL) prog = argv[0]; else prog++; fprintf(stderr, "Usage: %s ", prog); exit(1); } if ((fd = open(argv[1], O_RDWR)) < 0) err(1, "Cannot open '%s'.", argv[1]); if (fstat(fd, &st) < 0) err(1, "Cannot stat '%s'.", argv[1]); if (!S_ISCHR(st.st_mode)) errx(1, "'%s' must be a character special device.", argv[1]); if ((rv = bsd_label(fd, LABELSECTOR)) < 0) errx(1, "I/O error"); if (rv == 0) { warnx("Disk has no ahdi partitions"); return (2); } setupterm(NULL, STDOUT_FILENO, NULL); ptable.nparts = 0; ptable.parts = NULL; if ((ahdi_getparts(fd, &ptable, AHDI_BBLOCK, AHDI_BBLOCK) != 0) || (ptable.nparts == 0)) exit (1); edit_parts(fd, &ptable); return (0); } int edit_parts(int fd, ptable_t *ptable) { int scr_base = 0; int value; char *error, *new_id; for (;;) { error = NULL; if (clear_screen) tputs(clear_screen, 1, putchar); show_parts(ptable, scr_base); printf("\n\n"); printf("q : quit - don't update the disk\n"); printf("w : write changes to disk\n"); printf("> : next screen of partitions\n"); printf("< : previous screen of partitions\n"); printf(" : modify id of partition \n"); printf("\n\nCommand? "); fflush(stdout); switch (lex(&value)) { case T_EOF: exit(0); case T_INVAL: error = "Invalid command"; break; case T_QUIT : for (value = 0; value < ptable->nparts; value++) { if (ptable->parts[value].mod) { printf("\nThere are unwritten changes." " Quit anyway? [n] "); value = getchar(); if ((value == 'y') || (value == 'Y')) exit (0); while (value != '\n') value = getchar(); break; } } if (value == ptable->nparts) exit(0); break; case T_WRITE: error = "No changes to write"; for (value = 0; value < ptable->nparts; value++) { if (ptable->parts[value].mod) { update_disk(ptable, fd, value); error = ""; } } break; case T_NEXT : if ((scr_base + MAX_PSHOWN) < ptable->nparts) scr_base += MAX_PSHOWN; break; case T_PREV : scr_base -= MAX_PSHOWN; if (scr_base < 0) scr_base = 0; break; case T_NUMBER: if (value >= ptable->nparts) { error = "Not that many partitions"; break; } if ((new_id = get_id()) == NULL) { error = "Invalid id"; break; } strncpy(ptable->parts[value].id, new_id, 3); ptable->parts[value].mod = 1; scr_base = (value / MAX_PSHOWN) * MAX_PSHOWN; break; default : error = "Internal error - unknown token"; break; } if (error != NULL) { printf("\n\n%s", error); fflush(stdout); sleep(2); } } } int show_parts(ptable_t *ptable, int nr) { int i; part_t *p; u_int megs; if (nr >= ptable->nparts) return (0); /* Nothing to show */ printf("\n\n"); printf("nr root desc id start end MBs\n"); p = &ptable->parts[nr]; i = nr; for(; (i < ptable->nparts) && ((i - nr) < MAX_PSHOWN); i++, p++) { megs = ((p->end - p->start + 1) + (BLPM >> 1)) / BLPM; printf("%2d%s %8u %4u %s %8u %8u (%3u)\n", i, p->mod ? "*" : " ", p->rsec, p->rent, p->id, p->start, p->end, megs); } return (1); } int lex(int *value) { char c[1]; int rv, nch; rv = T_INVAL; *value = 0; for (;;) { if ((nch = read (0, c, 1)) != 1) { if (nch == 0) return (T_EOF); else return (rv); } switch (*c) { case 'q': rv = T_QUIT; goto out; case 'w': rv = T_WRITE; goto out; case '>': rv = T_NEXT; goto out; case '<': rv = T_PREV; goto out; default : if (isspace((unsigned char)*c)) { if (rv == T_INVAL) break; goto out; } else if (isdigit((unsigned char)*c)) { *value = (10 * *value) + *c - '0'; rv = T_NUMBER; } goto out; } } /* NOTREACHED */ out: /* * Flush rest of line before returning */ while (read (0, c, 1) == 1) if ((*c == '\n') || (*c == '\r')) break; return (rv); } char * get_id(void) { static char buf[5]; int n; printf("\nEnter new id: "); if (fgets(buf, sizeof(buf), stdin) == NULL) return (NULL); for (n = 0; n < 3; n++) { if (!isalpha((unsigned char)buf[n])) return (NULL); buf[n] = toupper((unsigned char)buf[n]); } buf[3] = '\0'; return (buf); } int bsd_label(int fd, u_int offset) { u_char *bblk; u_int nsec; int rv; nsec = (BBMINSIZE + (DEV_BSIZE - 1)) / DEV_BSIZE; bblk = disk_read(fd, offset, nsec); if (bblk) { u_int *end, *p; end = (u_int *)&bblk[BBMINSIZE - sizeof(struct disklabel)]; rv = 1; for (p = (u_int *)bblk; p < end; ++p) { struct disklabel *dl = (struct disklabel *)&p[1]; if ( ( (p[0] == NBDAMAGIC && offset == 0) || (p[0] == AHDIMAGIC && offset != 0) || (u_char *)dl - bblk == 7168 ) && dl->d_npartitions <= MAXPARTITIONS && dl->d_magic2 == DISKMAGIC && dl->d_magic == DISKMAGIC && dkcksum(dl) == 0 ) { rv = 0; break; } } free(bblk); } else rv = -1; return(rv); } int dkcksum(struct disklabel *dl) { u_short *start, *end, sum = 0; start = (u_short *)dl; end = (u_short *)&dl->d_partitions[dl->d_npartitions]; while (start < end) sum ^= *start++; return(sum); } void ahdi_cksum(void *buf) { unsigned short *p = (unsigned short *)buf; unsigned short csum = 0; int i; p[255] = 0; for(i = 0; i < 256; i++) csum += *p++; *--p = (0x1234 - csum) & 0xffff; } u_int ahdi_getparts(fd, ptable, rsec, esec) int fd; ptable_t *ptable; u_int rsec, esec; { struct ahdi_part *part, *end; struct ahdi_root *root; u_int rv; root = disk_read(fd, rsec, 1); if (!root) { rv = rsec + (rsec == 0); goto done; } if (rsec == AHDI_BBLOCK) end = &root->ar_parts[AHDI_MAXRPD]; else end = &root->ar_parts[AHDI_MAXARPD]; for (part = root->ar_parts; part < end; ++part) { u_int id; memcpy(&id, &part->ap_flg, sizeof (id)); if (!(id & 0x01000000)) continue; if ((id &= 0x00ffffff) == AHDI_PID_XGM) { u_int offs = part->ap_st + esec; rv = ahdi_getparts(fd, ptable, offs, esec == AHDI_BBLOCK ? offs : esec); if (rv) goto done; } else { part_t *p; u_int i = ++ptable->nparts; ptable->parts = realloc(ptable->parts, i * sizeof *ptable->parts); if (ptable->parts == NULL) { fprintf(stderr, "Allocation error\n"); rv = 1; goto done; } p = &ptable->parts[--i]; memcpy(&p->id, &id, sizeof (id)); p->start = part->ap_st + rsec; p->end = p->start + part->ap_size - 1; p->rsec = rsec; p->rent = part - root->ar_parts; p->mod = 0; } } rv = 0; done: free(root); return(rv); } void * disk_read(fd, start, count) int fd; u_int start, count; { char *buffer; off_t offset; size_t size; size = count * DEV_BSIZE; offset = start * DEV_BSIZE; if ((buffer = malloc(size)) == NULL) errx(1, "No memory"); if (lseek(fd, offset, SEEK_SET) != offset) { free(buffer); err(1, "Seek error"); } if (read(fd, buffer, size) != size) { free(buffer); err(1, "Read error"); exit(1); } return(buffer); } void update_disk(ptable_t *ptable, int fd, int pno) { struct ahdi_root *root; struct ahdi_part *apart; part_t *lpart; u_int rsec; int i; rsec = ptable->parts[pno].rsec; root = disk_read(fd, rsec, 1); /* * Look for additional mods on the same sector */ for (i = 0; i < ptable->nparts; i++) { lpart = &ptable->parts[i]; if (lpart->mod && (lpart->rsec == rsec)) { apart = &root->ar_parts[lpart->rent]; /* Paranoia.... */ if ((lpart->end - lpart->start + 1) != apart->ap_size) errx(1, "Updating wrong partition!"); apart->ap_id[0] = lpart->id[0]; apart->ap_id[1] = lpart->id[1]; apart->ap_id[2] = lpart->id[2]; lpart->mod = 0; } } if (rsec == 0) ahdi_cksum(root); disk_write(fd, rsec, 1, root); free(root); } void disk_write(fd, start, count, buf) int fd; u_int start, count; void *buf; { off_t offset; size_t size; size = count * DEV_BSIZE; offset = start * DEV_BSIZE; if (lseek(fd, offset, SEEK_SET) != offset) err(1, "Seek error"); if (write(fd, buf, size) != size) err(1, "Write error"); }