/* * gpart.c -- gpart main * * gpart (c) 1999-2001 Michail Brzitwa * Guess PC-type hard disk partitions. * * gpart is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published * by the Free Software Foundation; either version 2, or (at your * option) any later version. * * Created: 04.01.1999 * Modified: 11.06.1999 * Handle disk read errors. * Minor fixes. * * 29.06.1999 * Made every disk read/write buffer aligned to pagesize. * * 29.08.1999 * Default scan increment now 's'. * Extended ptbl boundary condition now depends on scan * increment. * * 26.02.2000 * Default scan increment 'h' again. * Fixed faulty head boundary condition. * Introduced ptbl entry editing after guess loop. * First scanned sector is no of sects/head, if no start * sector was given. * m_notinext now honoured. * Make a MBR backup. * Interactive mode now somehow works. * * 14.05.2000 * Made writing of guessed table also aligned. * Fixed stupid copy&paste bug in the check routine * (found by Bruno Bozza . * * 29.01.2001 * Extended partition type on an LBA disk now 0x0f instead * of 0x05. Changed some partition types (get_part_type). * When comparing partition types in extptbl links, try * to compare similarity, not equality (is_same_partition_type). * */ #include #include #include #include #include #include #include #include #include #include #include "gpart.h" static const char rcsid[] = "$Id: gpart.c,v 1.11 2001/02/07 18:08:08 mb Exp mb $"; static const char *gpart_version = PROGRAM " v" VERSION; int f_check = 0, f_verbose = 0, f_dontguess = 0, f_fast = 1; int f_getgeom = 1, f_interactive = 0, f_quiet = 0, f_testext = 1; int f_skiperrors = 1, berrno = 0; int (*boundary_fun)(disk_desc *,s64_t); unsigned long increment = 'h', gc = 0, gh = 0, gs = 0; s64_t skipsec = 0, maxsec = 0; FILE *log = 0; void usage() { FILE *fp = stderr; fprintf(fp,"Usage: %s [options] device\n",PROGRAM); fprintf(fp,"Options: [-b ][-C c,h,s][-c][-d][-E][-e][-f][-g][-h][-i]\n"); fprintf(fp," [-K ][-k <# of sectors>][-L][-l ]\n"); fprintf(fp," [-n ][-q][-s ][-t ]\n"); fprintf(fp," [-V][-v][-W ][-w ]\n"); fprintf(fp,"%s (c) 1999-2001 Michail Brzitwa .\n",gpart_version); fprintf(fp,"Guess PC-type hard disk partitions.\n\n"); if (! f_verbose) return; fprintf(fp,"Options:\n"); fprintf(fp," -b Save a backup of the original MBR to specified file.\n"); fprintf(fp," -C Set c/h/s to be used in the scan.\n"); fprintf(fp," -c Check/compare mode.\n"); fprintf(fp," -d Do not start the guessing loop.\n"); fprintf(fp," -E Do not try to identify extended partition tables.\n"); fprintf(fp," -e Do not skip disk read errors.\n"); fprintf(fp," -f Full scan.\n"); fprintf(fp," -g Do not try to get the disk geometry.\n"); fprintf(fp," -h Show this help.\n"); fprintf(fp," -i Run interactively (ask for confirmation).\n"); fprintf(fp," -K Scan only up to given sector.\n"); fprintf(fp," -k Skip sectors before scan.\n"); fprintf(fp," -L List available modules and their weights, then exit.\n"); fprintf(fp," -l Logfile name.\n"); fprintf(fp," -n Scan increment: number or 's' sector, 'h' head, 'c' cylinder.\n"); fprintf(fp," -q Run quiet (however log file is written if specified).\n"); fprintf(fp," -s Sector size to use (disable sector size probing).\n"); fprintf(fp," -t Name of a guessing module to be added.\n"); fprintf(fp," -V Show version.\n"); fprintf(fp," -v Verbose mode. Can be given more than once.\n"); fprintf(fp," -W Write guessed primary partition table to given device or file.\n"); fprintf(fp," -w Weight factor of module.\n"); fprintf(fp,"\n"); } void pr(int type,char *fmt,...) { va_list vl; static char msg[512]; va_start(vl,fmt); vsnprintf(msg,511,fmt,vl); va_end(vl); msg[511] = 0; switch (type) { case FATAL : g_mod_deleteall(); if (! f_quiet) fprintf(stderr,EM_FATALERROR,msg); if (log) { fprintf(log,EM_FATALERROR,msg); fclose(log); } exit(1); case ERROR : if (! f_quiet) fprintf(stderr,EM_SIMPLEERROR,msg); if (log) fprintf(log,EM_SIMPLEERROR,msg); break; case WARN : if (! f_quiet) fprintf(stderr,EM_WARNING,msg); if (log) fprintf(log,EM_WARNING,msg); break; case MSG : if (! f_quiet) fputs(msg,stdout); fflush(stdout); if (log) fputs(msg,log); break; } if (log) fflush(log); } byte_t *alloc(ssize_t s) { byte_t *p = (byte_t *)malloc(s); if (p == 0) pr(FATAL,EM_MALLOCFAILED,s); memset(p,0,s); return (p); } /* * read nsecs blocks of ssize bytes from fd */ ssize_t bread(int fd,byte_t *buf,size_t ssize,size_t nsecs) { ssize_t cs = 0, nr = 0; for ( ; nsecs > 0; nsecs--) { if ((nr = read(fd,buf,ssize)) == -1) { berrno = errno; return ((cs == 0) ? -1 : cs); } cs += nr; buf += nr; if (nr < ssize) break; } return (cs); } static int yesno(char *q) { int ch = 0; char buf[3]; pr(MSG,q); pr(MSG," %s : ",DM_YESNO); if (fgets(buf,3,stdin)) ch = *buf; pr(MSG,"\n"); return (strchr(DM_YES,ch) == 0 ? 0 : 1); } static long number_or_quit(char *m,long lo,long up) { char buf[32]; long num = -1; pr(MSG,m); pr(MSG,DM_NUMORQUIT,lo,up); if (fgets(buf,32,stdin)) { if (strchr(DM_QUIT,*buf)) return (-1); num = strtoul(buf,0,0); if (errno == ERANGE) return (-1); } return (num); } /* * get three comma separated strings. */ static int get_csep_arg(char *arg,char **p1,char **p2,char **p3) { char *p; if (p1) *p1 = arg; else return (0); if ((p = strchr(arg,',')) == 0) return (0); *p = 0; arg = p + 1; if (p2) *p2 = arg; else return (1); if (p3) { if ((p = strchr(arg,',')) == 0) return (0); *p = 0; *p3 = p + 1; } return (1); } /* * partition type list, taken from *BSD i386 fdisk, cfdisk etc. */ static char *get_part_type(int type) { int i; struct { int t; char *n; } ptypes[] = { { 0x00, "unused" }, { 0x01, "Primary DOS with 12 bit FAT" }, { 0x02, "XENIX / filesystem" }, { 0x03, "XENIX /usr filesystem" }, { 0x04, "Primary DOS with 16 bit FAT (<= 32MB)" }, { 0x05, "Extended DOS" }, { 0x06, "Primary 'big' DOS (> 32MB)" }, { 0x07, "OS/2 HPFS, NTFS, QNX or Advanced UNIX" }, { 0x08, "AIX filesystem" }, { 0x09, "AIX boot partition or Coherent" }, { 0x0A, "OS/2 Boot Manager or OPUS" }, { 0x0B, "DOS or Windows 95 with 32 bit FAT" }, { 0x0C, "DOS or Windows 95 with 32 bit FAT, LBA" }, { 0x0E, "Primary 'big' DOS (> 32MB, LBA)" }, { 0x0F, "Extended DOS, LBA" }, { 0x10, "OPUS" }, { 0x11, "Hidden DOS with 12 bit FAT" }, { 0x12, "Compaq Diagnostics" }, { 0x14, "Hidden DOS with 16 bit FAT (<= 32MB)" }, { 0x16, "Hidden 'big' DOS (> 32MB)" }, { 0x17, "OS/2 Boot Manager HPFS" }, { 0x18, "AST special Windows swap file" }, { 0x24, "NEC MS-DOS 3.x" }, { 0x3C, "PowerQuest PartitionMagic recovery partition" }, { 0x40, "VENIX 286" }, { 0x4D, "QNX4.x" }, { 0x4E, "QNX4.x 2nd part" }, { 0x4F, "QNX4.x 3rd part" }, { 0x50, "DM" }, { 0x51, "DM" }, { 0x51, "DM" }, { 0x52, "CP/M or Microport SysV/AT" }, { 0x55, "EZ Drive" }, { 0x56, "GB" }, { 0x61, "SpeedStor" }, { 0x63, "ISC UNIX, other System V/386, GNU HURD or Mach" }, { 0x64, "Novell Netware 2.xx" }, { 0x65, "Novell Netware 3.xx" }, { 0x70, "DiskSecure Multi-Boot" }, { 0x75, "PCIX" }, { 0x80, "Minix V1" }, { 0x81, "Minix V2/Linux" }, { 0x82, "Linux swap or Solaris/x86" }, { 0x83, "Linux ext2 filesystem" }, { 0x85, "Extended Linux" }, { 0x86, "FAT16 volume/stripe set" }, { 0x8E, "Linux LVM physical volume" }, { 0x93, "Amoeba filesystem" }, { 0x94, "Amoeba bad block table" }, { 0xA5, "FreeBSD/NetBSD/386BSD" }, { 0xA6, "OpenBSD" }, { 0xA7, "NEXTSTEP" }, { 0xB7, "BSDI BSD/386 filesystem" }, { 0xB8, "BSDI BSD/386 swap" }, { 0xC7, "Syrinx" }, { 0xDB, "Concurrent CPM or C.DOS or CTOS" }, { 0xE1, "SpeedStor 12-bit FAT extended" }, { 0xE3, "Speed" }, { 0xE4, "SpeedStor 16-bit FAT" }, { 0xEB, "BeOS fs" }, { 0xF1, "SpeedStor" }, { 0xF2, "DOS 3.3+ Secondary" }, { 0xF4, "SpeedStor" }, { 0xFD, "Linux raid autodetect" }, { 0xFE, "LANstep" }, { 0xFF, "BBT (Bad Blocks Table)" } }; for (i = 0; i < sizeof(ptypes)/sizeof(ptypes[0]); i++) if (type == ptypes[i].t) return (ptypes[i].n); return (0); } static int is_ext_parttype(dos_part_entry *p) { return (p->p_size && ((p->p_typ == 0x05) || (p->p_typ == 0x0F) || (p->p_typ == 0x85))); } static int is_sane_partentry(disk_desc *d,dos_part_entry *p,int c) { if (p->p_start >= d->d_nsecs) { if (c) pr(WARN,EM_PSTART2BIG,get_part_type(p->p_typ)); return (0); } if (p->p_size > d->d_nsecs) { if (c) pr(WARN,EM_PSIZE2BIG,get_part_type(p->p_typ)); return (0); } if (p->p_start + p->p_size > d->d_nsecs) { if (c) pr(WARN,EM_PEND2BIG,get_part_type(p->p_typ)); return (0); } if (p->p_flag && (p->p_flag != DOSPARTACTIVE)) { if (c) pr(WARN,EM_STRANGEPTYPE,get_part_type(p->p_typ)); return (0); } return (1); } static int is_real_parttype(dos_part_entry *p) { return (!is_ext_parttype(p) && p->p_typ && get_part_type(p->p_typ)); } static int no_of_ext_partitions(dos_part_entry *p) { dos_part_entry *t; int ne = 0; for (t = &p[0]; t < &p[NDOSPARTS]; t++) if (is_ext_parttype(t)) ne++; return (ne); } static int no_of_real_partitions(dos_part_entry *p) { dos_part_entry *t; int nr = 0; for (t = &p[0]; t < &p[NDOSPARTS]; t++) if (is_real_parttype(t)) nr++; return (nr); } /* * Test similarity of partition types */ static int is_same_partition_type(dos_part_entry *p1,dos_part_entry *p2) { int ret = 0; switch (p1->p_typ) { case 0x01: case 0x11: ret = (p2->p_typ == 0x06) || (p2->p_typ == 0x0E); break; case 0x06: case 0x0E: case 0x16: ret = (p2->p_typ == 0x06) || (p2->p_typ == 0x0E) || (p2->p_typ == 0x16); break; case 0x05: case 0x0F: ret = (p2->p_typ == 0x05) || (p2->p_typ == 0x0F); break; case 0x0B: case 0x0C: ret = (p2->p_typ == 0x0B) || (p2->p_typ == 0x0C); break; case 0x8E: case 0xFE: ret = (p2->p_typ == 0x8E) || (p2->p_typ == 0xFE); break; default : ret = p1->p_typ == p2->p_typ; break; } return (ret); } /* * detecting an extended ptbl isn't unambiguous, the boot code * preceding the ptbl should be zeroed but isn't always. The * ptbl should in theory contain one 'normal' entry, zero or * one link to the next extended ptbl and two or three zeroed * entries. */ static int is_ext_parttable(disk_desc *d,byte_t *buf) { int r, e; byte_t *magic; dos_part_entry *p, *t; p = (dos_part_entry *)(buf + DOSPARTOFF); magic = (byte_t *)&p[NDOSPARTS]; if (*(unsigned short *)magic != le16(DOSPTMAGIC)) return (0); /* * ptbl sanity checks. */ for (t = p; t < &p[NDOSPARTS]; t++) if (! is_sane_partentry(d,t,0)) return (0); /* * one real, zero or one extended and two or three unused * partition entries. */ r = no_of_real_partitions(p); e = no_of_ext_partitions(p); return ((r == 1) && ((e == 0) || (e == 1))); } static void fillin_dos_chs(disk_desc *d,dos_part_entry *p,s64_t offset) { unsigned long n; n = p->p_start; if (n > 1023 * d->d_dg.d_h * d->d_dg.d_s) { p->p_ssect = d->d_dg.d_s | ((1023 >> 2) & 0xc0); p->p_shd = d->d_dg.d_h - 1; p->p_scyl = 1023 & 0xff; } else { p->p_ssect = (n % d->d_dg.d_s) + 1; n /= d->d_dg.d_s; p->p_shd = n % d->d_dg.d_h; n /= d->d_dg.d_h; p->p_scyl = n & 0xff; p->p_ssect |= (n >> 2) & 0xc0; } n = p->p_size + p->p_start - 1; if (n > 1023 * d->d_dg.d_h * d->d_dg.d_s) { p->p_esect = d->d_dg.d_s | ((1023 >> 2) & 0xc0); p->p_ehd = d->d_dg.d_h - 1; p->p_ecyl = 1023 & 0xff; } else { p->p_esect = (n % d->d_dg.d_s) + 1; n /= d->d_dg.d_s; p->p_ehd = n % d->d_dg.d_h; n /= d->d_dg.d_h; p->p_ecyl = n & 0xff; p->p_esect |= (n >> 2) & 0xc0; } } static void u_to_chs(disk_desc *d,unsigned long u,long *c,long *h,long *s) { struct disk_geom *g = &d->d_dg; *c = *h = *s = 0; if (g->d_h && g->d_s && u) { *c = u / (g->d_h * g->d_s); *h = (u / g->d_s) % g->d_h; *s = u % g->d_s + 1; } } static int on_cyl_boundary(disk_desc *d,s64_t sec) { struct disk_geom *g = &d->d_dg; if (g->d_h && g->d_s) return ((sec % (g->d_h * g->d_s)) == 0); return (1); } static int on_head_boundary(disk_desc *d,s64_t sec) { struct disk_geom *g = &d->d_dg; if (g->d_s) return ((sec % g->d_s) == 0); return (1); } static void print_partition(disk_desc *d,dos_part_entry *p,int inset,s64_t offset) { long i, c = 0, h = 0, s = 0; s64_t size; char *ptyp = get_part_type(p->p_typ); #define indent(s) for (i = 0; i < s; i++) pr(MSG," ") size = p->p_size; s2mb(d,size); indent(inset); pr(MSG,PM_PT_TYPE,p->p_typ,p->p_typ,ptyp ? ptyp : "UNKNOWN"); if (p->p_flag == DOSPARTACTIVE) pr(MSG," (BOOT)"); pr(MSG,"\n"); indent(inset); pr(MSG,PM_PT_SIZE,size,(s64_t)p->p_size); size = p->p_start; size += offset; size += p->p_size; if (size) size -= 1; pr(MSG," s(%qd-%qd)\n",(s64_t)p->p_start + offset,size); indent(inset); pr(MSG,PM_PT_CHS, DOSCYL(p->p_scyl,p->p_ssect),p->p_shd,DOSSEC(p->p_ssect), DOSCYL(p->p_ecyl,p->p_esect),p->p_ehd,DOSSEC(p->p_esect)); if (size) u_to_chs(d,p->p_start + offset,&c,&h,&s); pr(MSG," (%ld/%ld/%ld)-",c,h,s); if (size) u_to_chs(d,p->p_start + offset + p->p_size - 1,&c,&h,&s); pr(MSG,"(%ld/%ld/%ld)r\n",c,h,s); if (f_verbose > 0) { indent(inset); pr(MSG,PM_PT_HEX); for (i = 0; i < sizeof(dos_part_entry); i++) pr(MSG," %02X",((byte_t *)p)[i]); pr(MSG,"\n"); } pr(MSG,"\n"); } static void print_ext_partitions(disk_desc *d,s64_t offset) { dos_part_table *pt = d->d_pt.t_ext; dos_part_entry *p; s64_t extst = 0; for ( ; pt; pt = pt->t_ext) { pr(MSG,PM_EXTPART); for (p = pt->t_parts; p < &pt->t_parts[NDOSPARTS + 1]; p++) if (is_real_parttype(p)) print_partition(d,p,1,offset + extst); for (p = pt->t_parts; p < &pt->t_parts[NDOSPARTS + 1]; p++) if (is_ext_parttype(p)) extst = p->p_start; } } static void print_ptable(disk_desc *d,dos_part_table *pt,int pr_ext) { int n; for (n = 0; n < NDOSPARTS; n++) { pr(MSG,PM_PRIMPART,n + 1); print_partition(d,&pt->t_parts[n],0,0); if (pr_ext && is_ext_parttype(&pt->t_parts[n])) print_ext_partitions(d,pt->t_parts[n].p_start); } } static void print_disk_desc(disk_desc *d) { s64_t s; pr(MSG,PM_DEVDESC1,d->d_dev,d->d_ssize); if (f_getgeom) { s = d->d_nsecs; s2mb(d,s); pr(MSG,PM_DEVDESC2, d->d_dg.d_c,d->d_dg.d_h,d->d_dg.d_s, d->d_lba ? "(LBA) " : " ", d->d_nsecs,s); } pr(MSG,"\n"); if (d->d_pt.t_magic != le16(DOSPTMAGIC)) pr(WARN,EM_STRANGEPTBLMAGIC,d->d_pt.t_magic); print_ptable(d,&d->d_pt,1); } static void print_mboot_block(disk_desc *d) { int n, m, cols = 16; byte_t *boot = d->d_pt.t_boot; pr(MSG,PM_MBRPRINT,d->d_dev); for (n = 0; n < DOSPARTOFF - cols; n += cols) { pr(MSG," %04X: ",n); for (m = n; m < n + cols; m++) pr(MSG," %02x",boot[m]); pr(MSG,"\n "); for (m = n; m < n + cols; m++) pr(MSG," %c ",isprint(boot[m]) ? boot[m] : '.'); pr(MSG,"\n"); } } static void read_part_table(disk_desc *d,s64_t sec,byte_t *where) { ssize_t rd; size_t psize; byte_t *ubuf, *buf; psize = getpagesize(); ubuf = alloc(MAXSSIZE + psize); buf = align(ubuf,psize); sec *= d->d_ssize; if (l64seek(d->d_fd,sec,SEEK_SET) == -1) pr(FATAL,EM_SEEKFAILURE,d->d_dev); if (d->d_ssize < 512) rd = bread(d->d_fd,buf,d->d_ssize,512 / d->d_ssize); else rd = bread(d->d_fd,buf,d->d_ssize,1); if (rd == -1) pr(FATAL,EM_PTBLREAD); memcpy(where,buf,512); free((void *)ubuf); } static void read_ext_part_table(disk_desc *d,dos_part_table *pt) { dos_part_entry *p, *ep; s64_t epsize, epstart, epoffset; int epcount; epsize = epstart = epoffset = epcount = 0; while (1) { ep = 0; for (p = pt->t_parts; p < &pt->t_parts[NDOSPARTS + 1]; p++) if (is_ext_parttype(p)) { if (ep == 0) { ep = p; break; } pr(ERROR,EM_TOOMANYEXTP); } if (ep == 0) return; if (++epcount > 128) /* arbitrary maximum */ { pr(ERROR,EM_TOOMANYLOGP,128); return; } if (epstart == 0) { epstart = ep->p_start; epsize = ep->p_size; epoffset = 0; } else epoffset = ep->p_start; if (epoffset > epsize) { pr(ERROR,EM_EPILLEGALOFS); return; } /* * link in new extended ptbl. */ pt->t_ext = (dos_part_table *)alloc(sizeof(dos_part_table)); read_part_table(d,epstart + epoffset,(pt = pt->t_ext)->t_boot); if (! is_ext_parttable(d,pt->t_boot)) { pr(ERROR,EM_INVXPTBL,epstart + epoffset); return; } } } static void free_disk_desc(disk_desc *d) { dos_part_table *pt; dos_guessed_pt *pg; void *t; for (pt = d->d_pt.t_ext; pt; ) { t = pt->t_ext; free((void *)pt); pt = t; } for (pt = d->d_gpt.t_ext; pt; ) { t = pt->t_ext; free((void *)pt); pt = t; } for (pg = d->d_gl; pg; ) { t = pg->g_next; free((void *)pg); pg = t; } free((void *)d); } static disk_desc *get_disk_desc(char *dev,int sectsize) { byte_t *ubuf, *buf; disk_desc *d; int psize, ssize; struct disk_geom *dg; psize = getpagesize(); ubuf = alloc(MAXSSIZE + psize); buf = align(ubuf,psize); d = (disk_desc *)alloc(sizeof(disk_desc)); /* * I don't care if the given name denotes a block or character * special file or just a regular file. */ if ((d->d_fd = open(dev,O_RDONLY)) == -1) pr(FATAL,EM_OPENFAIL,dev,strerror(errno)); /* * try to test for sector sizes (doesn't work under many systems). */ if (sectsize > MAXSSIZE) pr(FATAL,EM_WRONGSECTSIZE,MAXSSIZE); if (sectsize) { ssize = bread(d->d_fd,buf,sectsize,1); if (ssize != sectsize) pr(FATAL,EM_FAILSSIZEATTEMPT,sectsize); d->d_ssize = sectsize; } else { for (d->d_ssize = MINSSIZE; d->d_ssize <= MAXSSIZE; d->d_ssize *= 2) { if (l64seek(d->d_fd,0,SEEK_SET) == -1) pr(FATAL,EM_SEEKFAILURE,dev); ssize = bread(d->d_fd,buf,d->d_ssize,1); if (ssize == d->d_ssize) break; } if (ssize == -1) pr(FATAL,EM_CANTGETSSIZE,dev); } d->d_dev = dev; read_part_table(d,0,d->d_pt.t_boot); if (f_getgeom) { if ((dg = disk_geometry(d)) == 0) pr(FATAL,EM_CANTGETGEOM); memcpy(&d->d_dg,dg,sizeof(struct disk_geom)); /* * command line geometry overrides */ if (gc) d->d_dg.d_c = gc; if (gh) d->d_dg.d_h = gh; if (gs) d->d_dg.d_s = gs; } else { d->d_dg.d_c = gc; d->d_dg.d_h = gh; d->d_dg.d_s = gs; } if (d->d_dg.d_c < 1024) d->d_dosc = 1; if ((d->d_dg.d_h > 16) || (d->d_dg.d_s > 63)) d->d_lba = 1; d->d_nsecs = d->d_dg.d_c; d->d_nsecs *= d->d_dg.d_h; d->d_nsecs *= d->d_dg.d_s; read_ext_part_table(d,&d->d_pt); close(d->d_fd); free((void *)ubuf); return (d); } static void add_guessed_p(disk_desc *d,dos_part_entry *p,int cnt) { dos_guessed_pt *gpt; if (d->d_gl == 0) gpt = d->d_gl = (dos_guessed_pt *)alloc(sizeof(dos_guessed_pt)); else { for (gpt = d->d_gl; gpt->g_next; gpt = gpt->g_next) ; gpt->g_next = (dos_guessed_pt *)alloc(sizeof(dos_guessed_pt)); gpt = gpt->g_next; } gpt->g_ext = (cnt > 1); for ( ; cnt > 0; cnt--) memcpy(&gpt->g_p[cnt-1],&p[cnt-1],sizeof(dos_part_entry)); gpt->g_sec = d->d_nsb; } static g_module *get_best_guess(g_module **g,int count) { int mx, i; float bestg = 0.0; /* * up to now the best guess is simple that one which * reported the largest probability (if there are more * than one, the last one of them). */ for (mx = i = 0; i < count; i++) if (g[i]->m_guess * g[i]->m_weight > bestg) bestg = g[mx = i]->m_guess * g[i]->m_weight; return ((bestg > 0.0) ? g[mx] : 0); } static int mod_is_aligned(disk_desc *d,g_module *m) { s64_t al; switch (m->m_align) { case 'h' : return (on_head_boundary(d,d->d_nsb)); case 'c' : return (on_cyl_boundary(d,d->d_nsb)); case 1 : case 's' : return (1); default : if (m->m_align > 0) { al = d->d_nsb; al %= m->m_align; return (al == 0); } break; } return (1); } /* * the main guessing loop. */ static void do_guess_loop(disk_desc *d) { g_module *m, **guesses; unsigned long incr = 0; int nsecs, in_ext = 0, end_of_ext = 0, psize; ssize_t rd, bsize = d->d_ssize; s64_t bincr, noffset, start; byte_t *ubuf; if ((d->d_fd = open(d->d_dev,O_RDONLY)) == -1) pr(FATAL,EM_OPENFAIL,d->d_dev,strerror(errno)); /* * initialize modules. Each should return the minimum * size in bytes it wants to receive for a test. */ for (m = g_mod_head(); m; m = m->m_next) if (m->m_init) { int sz; if ((sz = (*m->m_init)(d,m)) <= 0) pr(ERROR,EM_MINITFAILURE,m->m_name); bsize = max(sz,bsize); } if (bsize % d->d_ssize) bsize += d->d_ssize - bsize % d->d_ssize; nsecs = bsize / d->d_ssize; switch (increment) { case 's' : incr = 1; break; case 'h' : incr = d->d_dg.d_s; break; case 'c' : incr = d->d_dg.d_s * d->d_dg.d_h; break; default : incr = increment; break; } if (incr == 0) incr = 1; boundary_fun = (incr == 1) ? on_head_boundary : on_cyl_boundary; psize = getpagesize(); ubuf = alloc(bsize + psize); d->d_sbuf = align(ubuf,psize); d->d_nsb = 0; bincr = incr * d->d_ssize; start = skipsec ? skipsec : d->d_dg.d_s; d->d_nsb = start - incr; start *= d->d_ssize; if (l64seek(d->d_fd,start,SEEK_SET) == -1) pr(FATAL,EM_SEEKFAILURE,d->d_dev); /* * do the work: read blocks, distribute to modules, check * for probable hits. */ guesses = (g_module **)alloc(g_mod_count() * sizeof(g_module *)); pr(MSG,DM_STARTSCAN); scanloop: while ((rd = bread(d->d_fd,d->d_sbuf,d->d_ssize,nsecs)) == bsize) { int mod, have_ext = 0; g_module *bg; s64_t sz, ofs; d->d_nsb += incr; noffset = 0; ofs = d->d_nsb; s2mb(d,ofs); if (maxsec && (d->d_nsb > maxsec)) break; /* * reset modules */ for (m = g_mod_head(); m; m = m->m_next) m->m_skip = 0; guessit: bg = 0; mod = 0; for (m = g_mod_head(); m; m = m->m_next) { if (m->m_skip || (in_ext && m->m_notinext) || !mod_is_aligned(d,m)) continue; /* * because a gmodule is allowed to seek on * d->d_fd the current file position must be * saved. */ memset(&m->m_part,0,sizeof(dos_part_entry)); m->m_guess = GM_NO; l64opush(d->d_fd); if ((*m->m_gfun)(d,m) && (m->m_guess * m->m_weight >= GM_PERHAPS)) guesses[mod++] = m; if ((sz = l64opop(d->d_fd)) != l64tell(d->d_fd)) l64seek(d->d_fd,sz,SEEK_SET); } /* * now fetch the best guess. */ if (mod && (bg = get_best_guess(guesses,mod))) { noffset = bg->m_part.p_size; fillin_dos_chs(d,&bg->m_part,0); } /* * extended partition begin? */ if (f_testext && boundary_fun(d,d->d_nsb) && (!bg || !bg->m_hasptbl) && is_ext_parttable(d,d->d_sbuf)) { dos_part_entry *p; int no_ext; p = (dos_part_entry *)(d->d_sbuf + DOSPARTOFF); no_ext = no_of_ext_partitions(p); if (! in_ext) { pr(MSG,PM_POSSIBLEEXTPART,ofs); in_ext = 1; end_of_ext = 0; noffset = 0; } else if (no_ext == 0) end_of_ext = 1; if (in_ext) { if (f_interactive) { if (yesno(DM_ACCEPTGUESS)) add_guessed_p(d,p,have_ext = NDOSPARTS); else if (mod && bg) { bg->m_skip = 1; goto guessit; } } else add_guessed_p(d,p,have_ext = NDOSPARTS); } } if (! have_ext && noffset) { sz = noffset; s2mb(d,sz); if (in_ext) pr(MSG," "); pr(MSG,PM_POSSIBLEPART,bg->m_desc ? bg->m_desc : bg->m_name,sz,ofs); if (f_verbose) print_partition(d,&bg->m_part,in_ext ? 1 : 0,0); if (f_interactive) if (! yesno(DM_ACCEPTGUESS)) { noffset = 0; if (mod && bg) { bg->m_skip = 1; goto guessit; } } if (noffset) { add_guessed_p(d,&bg->m_part,1); if (end_of_ext) in_ext = 0; } } /* * seek to next sectors to investigate (may seek * backwards). */ if (noffset && f_fast) { if (noffset % incr) noffset += incr - noffset % incr; d->d_nsb += noffset - incr; noffset -= nsecs; noffset *= d->d_ssize; if (l64seek(d->d_fd,noffset,SEEK_CUR) == -1) pr(FATAL,EM_SEEKFAILURE,d->d_dev); } else if (bincr) if (l64seek(d->d_fd,bincr - bsize,SEEK_CUR) == -1) pr(FATAL,EM_SEEKFAILURE,d->d_dev); } /* * short read? */ if ((rd > 0) && (rd < bsize)) if (d->d_nsb + nsecs + 1 < d->d_nsecs) { /* * short read not at end of disk */ pr(f_skiperrors ? WARN : FATAL,EM_SHORTBREAD,d->d_nsb,rd,bsize); noffset = l64tell(d->d_fd); noffset /= d->d_ssize; noffset *= d->d_ssize; if (l64seek(d->d_fd,noffset,SEEK_SET) == -1) pr(FATAL,EM_SEEKFAILURE,d->d_dev); d->d_nsb = l64tell(d->d_fd) / d->d_ssize - incr; goto scanloop; } if (rd == -1) { /* * EIO is ignored (skipping current sector(s)) */ if (f_skiperrors && (berrno == EIO)) { pr(WARN,EM_BADREADIO,d->d_nsb); noffset = l64tell(d->d_fd); noffset /= d->d_ssize; noffset += incr; noffset *= d->d_ssize; if (l64seek(d->d_fd,noffset,SEEK_SET) == -1) pr(FATAL,EM_SEEKFAILURE,d->d_dev); d->d_nsb = l64tell(d->d_fd) / d->d_ssize - incr; goto scanloop; } pr(FATAL,EM_READERROR,d->d_dev,d->d_nsb,strerror(berrno)); } pr(MSG,DM_ENDSCAN); if (guesses) free((void *)guesses); for (m = g_mod_head(); m; m = m->m_next) if (m->m_term) (*m->m_term)(d); free((void *)ubuf); close(d->d_fd); } static void edit_partition(disk_desc *d,dos_part_entry *p) { char ans[32]; int n; unsigned long val; while (1) { pr(MSG,DM_NOCHECKWARNING); pr(MSG,PM_EDITITEM1,p->p_start); pr(MSG,PM_EDITITEM2,p->p_size); pr(MSG,PM_EDITITEM3,p->p_typ,get_part_type(p->p_typ)); if ((n = number_or_quit(DM_EDITWHICHITEM,1,3)) < 0) break; if ((n < 1) || (n > 3)) continue; pr(MSG,"Enter value for %d : ",n); if (fgets(ans,32,stdin)) { val = strtoul(ans,0,0); switch (n) { case 1: p->p_start = val; fillin_dos_chs(d,p,0); break; case 2: p->p_size = val; fillin_dos_chs(d,p,0); break; case 3: p->p_typ = val & 0xFF; break; } } } } static int make_mbr_backup(disk_desc *d,char *bfile) { int fd, ret = 0; if ((fd = open(bfile,O_WRONLY|O_CREAT)) < 0) return (ret); if (write(fd,d->d_pt.t_boot,512) == 512) ret = 1; close(fd); return (ret); } static void write_primary_ptbl(disk_desc *d,char *dev) { struct stat sbuf; byte_t *ptbl, *uptbl; int fd, doesntexist = 0, n; uptbl = alloc(d->d_ssize + getpagesize()); ptbl = align(uptbl,getpagesize()); if (stat(dev,&sbuf) == -1) { if (errno != ENOENT) pr(FATAL,EM_STATFAILURE,dev,strerror(errno)); else doesntexist = 1; } fd = open(dev,O_WRONLY | (doesntexist ? O_CREAT | O_EXCL : 0),0660); if (fd == -1) pr(FATAL,EM_OPENFAIL,dev,strerror(errno)); if (l64seek(fd,0,SEEK_SET) == -1) pr(FATAL,EM_SEEKFAILURE,dev); /* * is there a guessed partition table? */ if (d->d_gpt.t_magic == le16(DOSPTMAGIC)) { /* * ask if table should be hand-edited */ if (yesno(DM_EDITPTBL)) while (1) { if ((n = number_or_quit(DM_EDITWHICHPART,1,NDOSPARTS)) < 0) break; if ((n >= 1) && (n <= NDOSPARTS)) edit_partition(d,&d->d_gpt.t_parts[n - 1]); else break; print_ptable(d,&d->d_gpt,0); } /* * ask for the active partition. */ while (1) { if ((n = number_or_quit(DM_ACTWHICHPART,1,NDOSPARTS)) < 0) break; if ((n >= 1) && (n <= NDOSPARTS) && get_part_type(d->d_gpt.t_parts[n].p_typ)) { d->d_gpt.t_parts[n - 1].p_flag = DOSPARTACTIVE; break; } } if (yesno(DM_WRITEIT)) { memcpy(ptbl,d->d_pt.t_boot,DOSPARTOFF); memcpy(ptbl + DOSPARTOFF,d->d_gpt.t_parts,NDOSPARTS * sizeof(dos_part_entry) + 2); if (write(fd,ptbl,d->d_ssize) != d->d_ssize) pr(FATAL,EM_PTBLWRITE); if (S_ISBLK(sbuf.st_mode) || S_ISCHR(sbuf.st_mode)) { sync(); sleep(1); sync(); reread_partition_table(fd); pr(WARN,DM_ASKTOREBOOT); } } else pr(MSG,DM_NOTWRITTEN); } close(fd); free((void *)uptbl); } static void warn_invalid(disk_desc *d,dos_part_entry *p,char *w) { if (f_verbose > 1) { pr(MSG,EM_PINVALID,w); print_partition(d,p,0,0); } } /* * after having gathered a list of possible partitions they * have to be checked for consistency. This routine must be * improved, it's too weird and is mistaken too often. */ static int check_partition_list(disk_desc *d) { dos_guessed_pt *gp, *prev; dos_part_entry *p, *rp, *ep, *lep; int n, npp, epp, maxp, in_ext; s64_t size, ofs; p = rp = ep = lep = 0; pr(MSG,DM_STARTCHECK); /* * 1. pass: discard overlapping entries. This means * that the first entry is assumed to be ok. */ ofs = 0; prev = 0; npp = 0; for (gp = d->d_gl; gp; gp = gp->g_next) { if (gp->g_ext || gp->g_inv) continue; p = &gp->g_p[0]; if (gp == d->d_gl) ofs = p->p_start + p->p_size; else { if (p->p_start < ofs) { /* * overlap. unlink and discard. */ prev->g_next = gp->g_next; free((void *)gp); gp = prev; npp++; } else ofs += p->p_size; } prev = gp; } if (npp) pr(WARN,EM_DISCARDOVLP,npp); /* * 2. pass: decide type of every partition, * marking inconsistent ones as invalid. */ size = 0; in_ext = npp = epp = 0; maxp = NDOSPARTS; for (n = 1, gp = d->d_gl; gp; gp = gp->g_next, n++) { if (gp->g_inv) continue; if (gp->g_ext) { if (gp->g_next == 0) { /* * ext ptbl without logical p. */ gp->g_inv = 1; warn_invalid(d,p,EM_P_EATEND); break; } if (! in_ext) { /* * new extended p. chain. */ if (no_of_ext_partitions(gp->g_p) == 0) { gp->g_inv = 1; warn_invalid(d,p,EM_P_EWLP); continue; } in_ext = 1; epp++; if (maxp >= NDOSPARTS) maxp--; /* * already had one? */ if (epp > 1) { gp->g_inv = 1; warn_invalid(d,p,EM_P_MTOE); continue; } } if (no_of_ext_partitions(gp->g_p) == 0) in_ext = 0; rp = 0; for (p = &gp->g_p[0]; p < &gp->g_p[NDOSPARTS]; p++) { if (is_real_parttype(p)) rp = p; else if (is_ext_parttype(p) && (n == 1)) size = p->p_start; } gp = gp->g_next; if (gp->g_ext) { /* * should not happen: a supposedly logical * partition which is an extended p itself. */ gp->g_inv = 1; warn_invalid(d,p,EM_P_LISAE); continue; } else gp->g_log = 1; /* * the p. type in the extended ptbl and the following * logical p. type must be identical. Also check size. */ p = &gp->g_p[0]; if (is_real_parttype(p) && is_same_partition_type(rp,p) && (rp->p_size >= p->p_size)) { if (! is_sane_partentry(d,p,1)) gp->g_inv = 1; else size += gp->g_p[0].p_size + 1; } else { gp->g_inv = 1; warn_invalid(d,p,EM_P_UTS); } } else if (! in_ext) { /* * primary entry. */ gp->g_prim = 1; p = &gp->g_p[0]; if (n == 1) size = p->p_start; if (npp++ >= maxp) { gp->g_inv = 1; warn_invalid(d,p,EM_P_2MANYPP); } else { if (! is_sane_partentry(d,p,1)) { gp->g_inv = 1; warn_invalid(d,p,EM_P_NOTSANE); } else size += p->p_size; } } else { /* * in_ext && !gp->g_ext. This means the end * of the logical partition chain hasn't been * found. Reset it. */ in_ext = 0; gp->g_inv = 1; warn_invalid(d,p,EM_P_ENDNOTF); } } if (epp > 1) pr(WARN,EM_TOOMANYXPTS,epp); if (npp > maxp) pr(WARN,EM_TOOMANYPPTS,maxp,npp); /* * 3. pass: check logical partition chain. Logical * partitions which seem ok but are not found in the * link chain are marked orphaned. */ in_ext = size = ofs = 0; lep = 0; for (gp = d->d_gl; gp; gp = gp->g_next) { if (gp->g_inv) continue; if (gp->g_ext) { if (! in_ext) { in_ext = 1; ofs = gp->g_sec; } rp = ep = 0; for (p = &gp->g_p[0]; p < &gp->g_p[NDOSPARTS]; p++) { if (is_real_parttype(p)) rp = p; else if (is_ext_parttype(p)) ep = p; } if (lep && rp) if (gp->g_sec != ofs + lep->p_start) gp->g_next->g_orph = 1; if (ep) lep = ep; gp = gp->g_next; } } /* * if the list was consistent the size of the whole * extended ptbl is equal to the end of the last eptbl * link. */ if (rp && lep) size = lep->p_start + lep->p_size; else size = ofs = 0; for (gp = d->d_gl; gp; gp = gp->g_next) { if (gp->g_ext) continue; p = &gp->g_p[0]; if (gp->g_log) pr(MSG," "); pr(MSG,"Partition(%s): ",get_part_type(p->p_typ)); if (gp->g_inv) pr(MSG,PM_G_INVALID); if (gp->g_orph) pr(MSG,PM_G_ORPHANED); if (gp->g_prim) pr(MSG,PM_G_PRIMARY); if (gp->g_log) pr(MSG,PM_G_LOGICAL); pr(MSG,"\n"); if (f_verbose > 1) print_partition(d,p,gp->g_log ? 1 : 0,0); } /* * now fill in the guessed primary partition table. */ in_ext = n = 0; memset(&d->d_gpt,0,sizeof(dos_part_table)); for (gp = d->d_gl; gp; gp = gp->g_next) { if (n >= NDOSPARTS) break; if (gp->g_inv) continue; if (gp->g_ext) { if (! in_ext) { in_ext = 1; if (size && ofs) { p = &d->d_gpt.t_parts[n++]; p->p_start = ofs; p->p_typ = 0x05; p->p_typ = d->d_lba ? 0x0F : 0x05; p->p_size = size; fillin_dos_chs(d,p,0); } } gp = gp->g_next; continue; } if (gp->g_prim) memcpy(&d->d_gpt.t_parts[n++],&gp->g_p[0],sizeof(dos_part_entry)); } /* * final step: re-check this table. If ok, set the * ptbl magic number which is the indicator for * write_primary_ptbl that it seems to be ok. */ ep = 0; npp = 0; for (n = 0; n < NDOSPARTS; n++) { p = &d->d_gpt.t_parts[n]; if (ep && p->p_typ) { if ((ep->p_start + ep->p_size > p->p_start) || ! is_sane_partentry(d,p,1)) { /* * zis is not funny. Perhaps the p. list * can be re-checked but for now only * inconsistencies are counted. */ npp++; if (f_verbose > 2) { pr(WARN,EM_PINCONS); print_partition(d,p,0,0); } } } ep = p; } if (npp == 0) { d->d_gpt.t_magic = le16(DOSPTMAGIC); pr(MSG,"Ok.\n"); } else pr(MSG,DM_NOOFINCONS,npp); return (npp); } /* * compare both existing and guessed partition tables. * The order of the ptbl entries is not important (the * physically first partition on disk can be in the last * ptbl slot). */ static int compare_parttables(disk_desc *d) { int ret, i, j, diff; byte_t *pr, *pg; ret = 0; for (i = 0; i < NDOSPARTS; i++) { pr = (byte_t *)&d->d_pt.t_parts[i]; for (j = 0; j < NDOSPARTS; j++) { pg = (byte_t *)&d->d_gpt.t_parts[j]; /* * the p_flag entry cannot be included * in the comparison. */ diff = memcmp(pr + 1,pg + 1,sizeof(dos_part_entry) - 1); if (diff == 0) break; } if (diff) ret++; } return (ret); } /* * main */ int main(int ac,char **av) { char *optstr = "b:C:cdEefghiK:k:Ll:n:qs:t:VvW:w:"; char *p1, *p2, *p3, *odev = 0, *backup = 0; int opt, sectsize = 0, no_of_incons = 0; disk_desc *d; g_mod_addinternals(); while ((opt = getopt(ac,av,optstr)) != -1) switch (opt) { case 'b' : backup = optarg; break; case 'C' : if (! get_csep_arg(optarg,&p1,&p2,&p3)) { usage(); return (EXIT_FAILURE); } gc = strtoul(p1,0,0); if (errno == ERANGE) pr(FATAL,EM_INVVALUE); gh = strtoul(p2,0,0); if (errno == ERANGE) pr(FATAL,EM_INVVALUE); gs = strtoul(p3,0,0); if (errno == ERANGE) pr(FATAL,EM_INVVALUE); break; case 'c' : f_check = 1; break; case 'd' : f_dontguess = 1; break; case 'E' : f_testext = 0; break; case 'e' : f_skiperrors = 0; break; case 'f' : f_fast = 0; break; case 'g' : f_getgeom = 0; break; case 'i' : f_interactive = 1; break; case 'K' : maxsec = strtoul(optarg,0,0); if ((maxsec <= 0) || (errno == ERANGE)) pr(FATAL,EM_INVVALUE); break; case 'k' : /* strtos64? */ skipsec = strtoul(optarg,0,0); if (errno == ERANGE) pr(FATAL,EM_INVVALUE); break; case 'n' : if ((*optarg == 's') || (*optarg == 'h') || (*optarg == 'c')) increment = *optarg; else { increment = strtoul(optarg,0,0); if (errno == ERANGE) pr(FATAL,EM_INVVALUE); } break; case 'l' : if (log) fclose(log); if ((log = fopen(optarg,"w")) == 0) pr(FATAL,EM_OPENLOG,optarg); break; case 'L' : g_mod_list(); return (EXIT_SUCCESS); case 'q' : f_quiet = 1; break; case 's' : if ((sectsize = atoi(optarg)) <= 0) pr(FATAL,"sector size must be >= 0"); break; case 't' : if (! g_mod_addexternal(optarg)) return (EXIT_FAILURE); break; case 'v' : f_verbose++; break; case 'V' : fprintf(stderr,"%s\n",gpart_version); return (EXIT_SUCCESS); case 'w' : if (! get_csep_arg(optarg,&p1,&p2,0)) { usage(); return (EXIT_FAILURE); } if (! g_mod_setweight(p1,atof(p2))) pr(FATAL,EM_NOSUCHMOD,p1); break; case 'W' : odev = optarg; break; case '?' : case 'h' : default : usage(); return (EXIT_FAILURE); } if ((optind + 1) != ac) { usage(); return (EXIT_FAILURE); } if (f_dontguess) f_check = 0; if (f_check) { f_quiet = 1; f_dontguess = 0; odev = 0; } if (f_quiet) f_interactive = 0; sync(); d = get_disk_desc(av[optind],sectsize); if (f_verbose > 0) print_disk_desc(d); if (f_verbose > 2) print_mboot_block(d); if (! f_dontguess) { sleep(1); sync(); do_guess_loop(d); no_of_incons = check_partition_list(d); pr(MSG,DM_GUESSEDPTBL); print_ptable(d,&d->d_gpt,0); if ((no_of_incons == 0) && f_check) no_of_incons = compare_parttables(d); if ((no_of_incons == 0) && odev) { if (backup) make_mbr_backup(d,backup); write_primary_ptbl(d,odev); } } free_disk_desc(d); if (log) fclose(log); return (f_check ? no_of_incons : 0); }