blob: 80c0a4698a5085cffced3f9d9a2006426ca9bd0a [file] [log] [blame]
/*
* rescuept - Andries Brouwer - aeb@cwi.nl - 1999
*
* This may be distributed under the GPL.
*
* call: rescuept /dev/xxx
*
* The output is a proposed partition table, in the
* form of input to sfdisk. Typical use:
*
* ./rescuept /dev/xxx > xxx.pt
* now look at xxx.pt to see whether it resembles what
* you expected, and possibly edit the partition types;
* if you are satisfied, then
* sfdisk /dev/xxx < xxx.pt
* will restore your partition table. If you are cautious, use
* sfdisk /dev/xxx -O xxx.old < xxx.pt
* so that the original state can be retrieved using
* sfdisk /dev/xxx -I xxx.old
*
* Here xxx stands for hda or hdb or sda or sdb or ...
*/
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#ifndef BLKGETSIZE
#define BLKGETSIZE _IO(0x12,96)
#endif
char *progname;
char *device;
#define MAXPARTITIONS 100
#define MAXPAGESZ 65536
#define BUFSZ 1024000
#define BUFSECS (BUFSZ/512)
char buf[BUFSZ];
int bufstart = -1;
typedef unsigned int uint32;
typedef int sint32;
typedef unsigned short uint16;
typedef short sint16;
typedef unsigned char uchar;
void read_sectors(int fd, char *buf, int sectornr, int sectorct) {
extern long long llseek();
long long offset;
int n;
offset = sectornr;
offset *= 512;
if (llseek(fd, offset, SEEK_SET) != offset) {
fprintf(stderr, "%s: llseek error\n", progname);
exit(1);
}
n = read(fd, buf, sectorct*512);
if (n != sectorct*512) {
if (n == -1)
perror("read");
fprintf(stderr, "%s: error reading sectors %d-%d\n",
progname, sectornr, sectornr+sectorct-1);
exit(1);
}
}
/*
* Partition table stuff
*/
struct partition {
unsigned char bootable; /* 0 or 0x80 */
uchar begin_chs[3];
unsigned char sys_type;
uchar end_chs[3];
unsigned int start_sect; /* starting sector counting from 0 */
unsigned int nr_sects; /* nr of sectors in partition */
};
int
is_extended(unsigned char sys_type) {
return (sys_type == 0x5 || sys_type == 0xf || sys_type == 0x85);
}
/*
* List of (extended) partition table sectors found
*/
struct epts {
int secno;
char pt4[64];
} epts[MAXPARTITIONS];
int eptsct;
void addepts(int secno, char *data) {
if (eptsct >= MAXPARTITIONS)
return; /* ignore */
epts[eptsct].secno = secno;
memcpy(epts[eptsct].pt4, data+512-66, 64);
eptsct++;
}
/*
* List of guessed partitions
*/
struct pt {
int pno;
int start;
int size;
unsigned char type;
} pts[MAXPARTITIONS];
int partno;
void addpart(int start, int size, unsigned char type) {
if (partno >= MAXPARTITIONS)
return; /* ignore */
pts[partno].start = start;
pts[partno].size = size;
pts[partno].type = type;
partno++;
}
void outparts() {
int i;
for(i=0; i<partno; i++)
printf("%s%d : start=%9d, size=%8d, Id=%2x\n",
device, pts[i].pno,
pts[i].start, pts[i].size, pts[i].type);
}
void outmsg(char *msg, int start, int nextstart, unsigned char type) {
printf("# %5d MB %16s (type %2x): sectors %9d-%9d\n",
((nextstart-start)+1024)/2048, msg, type, start, nextstart-1);
}
int
create_extended_partition(int fd, int secno, int size) {
int sec = secno;
int cursec = secno;
int pno = partno; /* number of extd partition */
int ei = eptsct-1;
unsigned char type = 0x5;
int lastseen = secno;
int ok = 0;
if (epts[ei].secno != secno) {
fprintf(stderr, "%s: program bug\n", progname);
exit(1);
}
outmsg("candidate ext pt", secno, secno+1, type);
addpart(secno, 1, type); /* size to be filled in later */
while(1) {
char buf[512];
struct partition *p1, *p2, *pr, *pe;
p1 = (struct partition *)(& epts[ei].pt4[0]);
p2 = (struct partition *)(& epts[ei].pt4[16]);
/* for the time being we just ignore the rest */
if (is_extended(p1->sys_type)) {
pr = p2;
pe = p1;
} else if (is_extended(p2->sys_type)) {
pr = p1;
pe = p2;
} else if (p1->sys_type == 0) {
pr = p2;
pe = 0;
} else if (p2->sys_type == 0) {
pr = p1;
pe = 0;
} else
break;
/* first handle the real partition, if any */
if (pr->sys_type != 0) {
int ss = cursec + pr->start_sect;
int es = ss + pr->nr_sects;
outmsg("found in ept", ss, es, pr->sys_type);
addpart(ss, pr->nr_sects, pr->sys_type);
if (lastseen < es - 1)
lastseen = es - 1;
if (lastseen >= size)
break;
}
/* then the extended link */
if (!pe) {
ok = 1;
break;
}
type = pe->sys_type;
cursec = sec + pe->start_sect;
if (cursec >= size)
break;
read_sectors(fd, buf, cursec, 1);
addepts(cursec, buf);
ei = eptsct-1;
}
if (!ok || lastseen == secno) {
printf("# retracted\n");
partno = pno;
return 0;
}
pts[pno].type = type;
pts[pno].size = lastseen+1-secno;
outmsg("extended part ok", secno, lastseen+1, type);
return lastseen;
}
/*
* Recognize an ext2 superblock
*/
#define EXT2_SUPER_MAGIC 0xEF53
struct ext2_super_block {
uint32 s_inodes_count; /* 0: Inodes count */
uint32 s_blocks_count; /* 4: Blocks count */
uint32 s_r_blocks_count; /* 8: Reserved blocks count */
uint32 s_free_blocks_count; /* 12: Free blocks count */
uint32 s_free_inodes_count; /* 16: Free inodes count */
uint32 s_first_data_block; /* 20: First Data Block */
uint32 s_log_block_size; /* 24: Block size */
sint32 s_log_frag_size; /* 28: Fragment size */
uint32 s_blocks_per_group; /* 32: # Blocks per group */
uint32 s_frags_per_group; /* 36: # Fragments per group */
uint32 s_inodes_per_group; /* 40: # Inodes per group */
uint32 s_mtime; /* 44: Mount time */
uint32 s_wtime; /* 48: Write time */
uint16 s_mnt_count; /* 52: Mount count */
sint16 s_max_mnt_count; /* 54: Maximal mount count */
uint16 s_magic; /* 56: Magic signature */
uint16 s_state; /* 58: File system state */
uint16 s_errors; /* 60: Behaviour when detecting errors */
uint16 s_minor_rev_level; /* 62: minor revision level */
uint32 s_lastcheck; /* 64: time of last check */
uint32 s_checkinterval; /* 68: max. time between checks */
uint32 s_creator_os; /* 72: OS */
uint32 s_rev_level; /* 76: Revision level */
uint16 s_def_resuid; /* 80: Default uid for reserved blocks */
uint16 s_def_resgid; /* 82: Default gid for reserved blocks */
/* more stuff in later versions - especially s_block_group_nr is useful */
uint32 s_first_ino; /* 84: First non-reserved inode */
uint16 s_inode_size; /* 88: size of inode structure */
uint16 s_block_group_nr; /* 90: block group # of this superblock */
uint32 s_feature_compat; /* 92: compatible feature set */
uint32 s_feature_incompat; /* 96: incompatible feature set */
uint32 s_feature_ro_compat; /* 100: readonly-compatible feature set */
uchar s_uuid[16]; /* 104: 128-bit uuid for volume */
char s_volume_name[16]; /* 120: volume name */
char s_last_mounted[64]; /* 136: directory where last mounted */
uint32 s_algorithm_usage_bitmap;/* 200: For compression */
uchar s_prealloc_blocks; /* 204: Nr of blocks to try to preallocate*/
uchar s_prealloc_dir_blocks; /* 205: Nr to preallocate for dirs */
uchar s_reserved[818]; /* 206-1023 */
};
/*
* Heuristic to weed out false alarms for ext2 superblocks.
* Recompile this after 2005, of if you destroy things that
* have not been written the past ten years.
*/
#define YEAR (60*60*24*365)
#define LOWERLIMIT (1992-1970)*YEAR
#define UPPERLIMIT (2005-1970)*YEAR
int
is_time(uint32 t) {
return (t >= LOWERLIMIT && t <= UPPERLIMIT);
}
int
is_ztime(uint32 t) {
return (t == 0 || (t >= LOWERLIMIT && t <= UPPERLIMIT));
}
/*
* Recognize a FAT filesystem
*/
struct fat_boot_sector_start {
uchar jump_code[3]; /* 0: Bootstrap short or near jump */
/* usually jump code (e.g. eb 3e or eb 58) + nop (0x90) */
uchar system_id[8]; /* 3: OEM Name */
/* fat16: MSDOS5.0 or MSWIN4.0 or ... */
/* fat32: MSWIN4.1 (=W95 OSR2) */
/* BIOS Parameter Block (BPB) */
uchar sector_size[2]; /* 11: bytes/sector (usually 512 or 2048) */
uchar cluster_size; /* 13: sectors/cluster (a power of two in 1..128) */
uint16 reserved; /* 14: reserved sectors (I see 1 for FAT16, 17 for FAT32) */
/* The # of sectors preceding the first FAT,
including the boot sector, so at least 1 */
uchar fats; /* 16: # of copies of FAT (usually 2) */
uchar dir_entries[2]; /* 17: max # of root directory entries (n/a for FAT32) */
/* (usually 512; 0 for FAT32) */
uchar sectors[2]; /* 19: total # of sectors (in <32MB partn) or 0 */
uchar media; /* 21: media code (0xf8 for hard disks) */
uint16 fat_length; /* 22: sectors/FAT (n/a: 0 for FAT32) */
uint16 secs_track; /* 24: S = # sectors/track (in 1..63) */
uint16 heads; /* 26: H = # of heads (in 1..255) */
uint32 hidden; /* 28: # of hidden sectors in partition, before boot sector */
/* (offset from cyl boundary - often equal to S) */
uint32 total_sect; /* 32: # of sectors (if sectors == 0) */
};
/* Media descriptor byte:
f8 hard disk
Floppy types:
f0 3.5" 36/2/80 2880k
f0 3.5" 18/2/80 1440k
f9 3.5" 9/2/80 720k
f9 5.25" 15/2/80 1200k
fa both 9/1/80 320k
fb both 9/2/80 640k
fc 5.25" 9/1/40 180k
fd 5.25" 9/2/40 360k
fe 5.25" 8/1/40 160k
ff 5.25" 8/2/40 320k
Conclusion: this byte does not differentiate between 3.5" and 5.25",
it does not give the capacity or the number of sectors per track.
However, maybe C and H can be derived.
*/
struct fat_boot_sector_middle { /* offset 36-61 for FAT16, 64-89 for FAT32 */
/* Extended BIOS Parameter Block */
uchar drive_number; /* 0: logical drive number of partition */
/* (typically 0 for floppy, 0x80 for each disk) */
uchar current_head; /* Originally: track containing boot record. (Usually 0)
For WNT: bit 0: dirty: chkdsk must be run
bit 1: also run surface scan */
uchar extd_signature; /* 2: extended signature (0x29) */
/* WNT requires either 0x28 or 0x29 */
uchar serial_nr[4]; /* 3: serial number of partition */
uchar volume_name[11];/* 7: volume name of partition */
/* WNT stores the volume label as a special file
in the root directory */
uchar fs_name[8]; /* 18: filesystem name (FAT12, FAT16, FAT32) */
};
struct fat16_boot_sector {
struct fat_boot_sector_start s; /* 0-35 */
struct fat_boot_sector_middle m; /* 36-61 */
uchar boot_code[448]; /* 62-509 */
uchar signature[2]; /* 510-511: aa55 */
};
struct fat32_boot_sector {
struct fat_boot_sector_start s; /* 0-35 */
uint32 fat32_length; /* 36: sectors/FAT */
uint16 flags; /* 40: bit 7: fat mirroring, low 4: active fat */
/* If mirroring is disabled (bit8 set) the FAT
info is only written to the active FAT copy. */
uchar version[2]; /* 42: major, minor filesystem version */
uint32 root_cluster; /* 44: first cluster in root directory */
uint16 info_sector; /* 48: filesystem info sector # relative
to partition start (usually 1) */
uint16 backup_boot; /* 50: backup boot sector # relat. to part. start */
uint16 reserved2[6]; /* 52-63: Unused */
struct fat_boot_sector_middle m; /* 64-89 */
uchar boot_code[420]; /* 90-509 */
uchar signature[2]; /* 510-511: aa55 */
};
/*
* The boot code contains message strings ("Invalid system disk")
* but these are often localized ("Ongeldige diskette ").
* After these messages one finds two or three filenames.
* (MSDOS 6.2: "\r\nNon-System disk or disk error\r\n"
* "Replace and press any key when ready\r\n", "IO SYS", "MSDOS SYS")
* (W95: "IO SYS", "MSDOS SYS", "WINBOOT SYS")
* In all cases the sector seems to end with 0, 0, 55, aa.
*
* Random collection of messages (closed by \0377 or 0):
* "\r\nInvalid system disk"
* "\r\nOngeldige diskette "
* "\r\nDisk I/O error"
* "\r\nI/O-fout "
* "\r\nReplace the disk, and then press any key\r\n"
* "\r\nVervang de diskette en druk op een toets\r\n"
* This seems to suggest that the localized strings have the same length.
*
* "Non-System disk or disk error"
* "Replace and press any key when ready"
* "Disk Boot failure"
*
* "BOOT: Couldn't find NTLDR"
* "I/O error reading disk"
* "Please insert another disk"
*/
struct fat32_boot_fsinfo {
uint32 signature1; /* 41 61 52 52 */
uchar unknown1[480];
uint32 signature2; /* 61 41 72 72 0x61417272L */
uint32 free_clusters; /* Free cluster count. -1 if unknown */
uint32 next_cluster; /* Most recently allocated cluster.
* Unused under Linux. */
uchar unknown2[14];
uchar signature[2]; /* 510-511: aa55 */
};
struct msdos_dir_entry {
uchar name[8],ext[3]; /* name and extension */
uchar attr; /* attribute bits */
uchar lcase; /* Case for base and extension */
uchar ctime_ms; /* Creation time, milliseconds */
uint16 ctime; /* Creation time */
uint16 cdate; /* Creation date */
uint16 adate; /* Last access date */
uint16 starthi; /* High 16 bits of cluster in FAT32 */
uint16 time,date,start;/* time, date and first cluster */
uint32 size; /* file size (in bytes) */
};
/* New swap space */
struct swap_header_v1 {
char bootbits[1024]; /* Space for disklabel etc. */
unsigned int version;
unsigned int last_page;
unsigned int nr_badpages;
unsigned int padding[125];
unsigned int badpages[1];
};
struct unixware_slice {
unsigned short slice_type;
unsigned short slice_flags;
unsigned int start;
unsigned int size;
};
struct bsd_disklabel {
uchar d_magic[4];
uchar d_stuff1[4];
uchar d_typename[16]; /* type name, e.g. "eagle" */
uchar d_packname[16]; /* pack identifier */
uint32 d_secsize; /* # of bytes per sector */
uint32 d_nsectors; /* # of data sectors per track */
uint32 d_ntracks; /* # of tracks per cylinder */
uint32 d_ncylinders; /* # of data cylinders per unit */
uint32 d_secpercyl; /* # of data sectors per cylinder */
uint32 d_secperunit; /* # of data sectors per unit */
uchar d_stuff2[68];
uchar d_magic2[4]; /* the magic number (again) */
uint16 d_checksum; /* xor of data incl. partitions */
/* filesystem and partition information: */
uint16 d_npartitions; /* number of partitions in following */
uint32 d_bbsize; /* size of boot area at sn0, bytes */
uint32 d_sbsize; /* max size of fs superblock, bytes */
struct bsd_partition { /* the partition table */
uint32 p_size; /* number of sectors in partition */
uint32 p_offset; /* starting sector */
uchar p_stuff[8];
} d_partitions[16]; /* 16 is for openbsd */
};
int
main(int argc, char **argv){
int i,j,fd;
long size;
int pagesize, pagesecs;
unsigned char *bp;
struct ext2_super_block *e2bp;
struct fat16_boot_sector *fat16bs;
struct fat32_boot_sector *fat32bs;
progname = argv[0];
if (argc != 2) {
fprintf(stderr, "call: %s device\n", progname);
exit(1);
}
device = argv[1];
fd = open(device, O_RDONLY);
if (fd < 0) {
perror(device);
fprintf(stderr, "%s: could not open %s\n", progname, device);
exit(1);
}
if (ioctl(fd, BLKGETSIZE, &size)) {
struct stat s;
perror("BLKGETSIZE");
fprintf(stderr, "%s: could not get device size\n", progname);
if (stat(device, &s)) {
fprintf(stderr, "and also stat fails. Aborting.\n");
exit(1);
}
size = s.st_size / 512;
}
pagesize = getpagesize();
if (pagesize <= 0)
pagesize = 4096;
else if (pagesize > MAXPAGESZ) {
fprintf(stderr, "%s: ridiculous pagesize %d\n", progname, pagesize);
exit(1);
}
pagesecs = pagesize/512;
printf("# partition table of %s\n", device);
printf("# total size %d sectors\n", size);
printf("unit: sectors\n");
for(i=0; i<size; i++) {
if (i/BUFSECS != bufstart) {
int len, secno;
bufstart = i/BUFSECS;
secno = bufstart*BUFSECS;
len = BUFSECS;
if (size - secno < len)
len = size - secno;
len = (len / 2)*2; /* avoid reading the last (odd) sector */
read_sectors(fd, buf, secno, len);
}
j = i % BUFSECS;
bp = buf + 512 * j;
if (bp[510] == 0x55 && bp[511] == 0xAA) {
char *cp = bp+512-2-64;
int j;
if (i==0)
continue; /* the MBR is supposed to be broken */
/* Unfortunately one finds extended partition table sectors
that look just like a fat boot sector, except that the
partition table bytes have been overwritten */
/* typical FAT32 end: "nd then press ...", followed by
IO.SYS and MSDOS.SYS and WINBOOT.SYS directory entries.
typical extd part tab end: 2 entries, 32 nul bytes */
for(j=0; j<32; j++)
if (cp[32+j])
goto nonzero;
addepts(i, bp);
if (i > 0) {
j = create_extended_partition(fd, i, size);
if (j && j > i)
i = j; /* skip */
}
continue;
nonzero:
fat16bs = (struct fat16_boot_sector *) bp;
if (fat16bs->s.media == 0xf8 &&
fat16bs->m.extd_signature == 0x29 &&
!strncmp(fat16bs->m.fs_name, "FAT", 3)) {
int lth;
lth = fat16bs->s.sectors[0] +
fat16bs->s.sectors[1]*256;
if (lth) {
outmsg("small fat partition", i, i+lth, 0x1);
addpart(i, lth, 0x1);
} else {
lth = fat16bs->s.total_sect;
outmsg("fat partition", i, i+lth, 0x6);
addpart(i, lth, 0x6);
}
i = i+lth-1; /* skip */
continue;
}
fat32bs = (struct fat32_boot_sector *) bp;
if (fat32bs->s.media == 0xf8 &&
fat32bs->m.extd_signature == 0x29 &&
!strncmp(fat32bs->m.fs_name, "FAT32 ", 8)) {
int lth = fat32bs->s.total_sect;
outmsg("fat32 partition", i, i+lth, 0xb); /* or 0xc */
addpart(i, lth, 0xb);
i = i+lth-1; /* skip */
continue;
}
}
if (!strncmp(bp+502, "SWAP-SPACE", 10)) {
char *last;
int ct;
int ss = i-pagesecs+1;
int es;
char buf2[MAXPAGESZ];
read_sectors(fd, buf2, ss, pagesecs);
for (last = buf2+pagesize-10-1; last > buf2; last--)
if (*last)
break;
for (ct = 7; ct >= 0; ct--)
if (*last & (1<<ct))
break;
es = ((last - buf2)*8 + ct + 1)*pagesecs + ss;
if (es <= size) {
outmsg("old swap space", ss, es, 0x82);
addpart(ss, es-ss, 0x82);
i = es-1; /* skip */
continue;
}
}
if (!strncmp(bp+502, "SWAPSPACE2", 10)) {
int ss = i-pagesecs+1;
int es, lth;
char buf2[MAXPAGESZ];
struct swap_header_v1 *p;
read_sectors(fd, buf2, ss, pagesecs);
p = (struct swap_header_v1 *) buf2;
lth = (p->last_page + 1)* pagesecs;
es = ss + lth;
if (es <= size) {
outmsg("new swap space", ss, es, 0x82);
addpart(ss, lth, 0x82);
i = es-1; /* skip */
continue;
}
}
e2bp = (struct ext2_super_block *) bp;
if (e2bp->s_magic == EXT2_SUPER_MAGIC && is_time(e2bp->s_mtime)
&& is_time(e2bp->s_wtime) && is_ztime(e2bp->s_lastcheck)
&& e2bp->s_log_block_size <= 10 /* at most 1 MB blocks */) {
char buf[512];
struct ext2_super_block *bp2;
int ss, sz, es, gsz, j;
ss = i-2;
sz = (e2bp->s_blocks_count << (e2bp->s_log_block_size + 1));
gsz = (e2bp->s_blocks_per_group << (e2bp->s_log_block_size + 1));
if (e2bp->s_block_group_nr > 0)
ss -= gsz * e2bp->s_block_group_nr;
es = ss + sz;
if (ss > 0 && es > i && es <= size) {
if (e2bp->s_block_group_nr == 0) {
outmsg("ext2 partition", ss, es, 0x83);
addpart(ss, es-ss, 0x83);
i = es-1; /* skip */
continue;
}
/* maybe we jumped into the middle of a partially
obliterated ext2 partition? */
printf("# sector %d looks like an ext2 superblock copy #%d;\n"
"# in a partition covering sectors %d-%d\n",
i, e2bp->s_block_group_nr, ss, es-1);
for (j=1; j<=e2bp->s_block_group_nr; j++) {
read_sectors(fd, buf, i-j*gsz, 1);
bp2 = (struct ext2_super_block *) buf;
if (bp2->s_magic != EXT2_SUPER_MAGIC ||
bp2->s_block_group_nr !=
e2bp->s_block_group_nr - j)
break;
}
if (j == 1)
printf("# however, sector %d doesnt look like a sb.\n",
i-gsz);
else if (j <= e2bp->s_block_group_nr)
printf("# also the preceding %d block groups seem OK\n"
"# but before that things seem to be wrong.\n",
j-1);
else {
printf("# found all preceding superblocks OK\n"
"# Warning: overlapping partitions?\n");
outmsg("ext2 partition", ss, es, 0x83);
addpart(ss, es-ss, 0x83);
i = es-1; /* skip */
continue;
}
}
}
if (bp[4] == 0x0d && bp[5] == 0x60 &&
bp[6] == 0x5e && bp[7] == 0xca && /* CA5E600D */
bp[156] == 0xee && bp[157] == 0xde &&
bp[158] == 0x0d && bp[159] == 0x60) /* 600DDEEE */ {
int ss, es;
struct unixware_slice *u;
printf("# Unixware partition seen\n");
u = (struct unixware_slice *)(bp + 216);
if (u->slice_type == 5 /* entire disk */
&& (u->slice_flags & 0x200)) /* valid */ {
ss = u->start;
es = u->start + u->size;
outmsg("Unixware ptn", ss, es, 0x63);
addpart(ss, es-ss, 0x63);
i = es-1;
continue;
} else
printf("# Unrecognized details\n");
}
/* bsd disk magic 0x82564557UL */
if (bp[0] == 0x57 && bp[1] == 0x45 && bp[2] == 0x56 && bp[3] == 0x82) {
int ss, es, npts, j;
struct bsd_disklabel *l;
struct bsd_partition *p;
printf("# BSD magic seen in sector %d\n", i);
l = (struct bsd_disklabel *) bp;
if (l->d_magic2[0] != 0x57 || l->d_magic2[1] != 0x45 ||
l->d_magic2[2] != 0x56 || l->d_magic2[3] != 0x82)
printf("# 2nd magic bad - ignored this sector\n");
else if ((npts = l->d_npartitions) > 16)
printf("# strange number (%d) of subpartitions - "
"ignored this sector\n", npts);
else {
for (j=0; j<npts; j++) {
p = &(l->d_partitions[j]);
if (p->p_size)
printf("# part %c: size %9d, start %9d\n",
'a'+j, p->p_size, p->p_offset);
}
ss = l->d_partitions[2].p_offset;
es = ss + l->d_partitions[2].p_size;
if (ss != i-1)
printf("# strange start of whole disk - "
"ignored this sector\n");
else {
/* FreeBSD 0xa5, OpenBSD 0xa6, NetBSD 0xa9, BSDI 0xb7 */
/* How to distinguish? */
outmsg("BSD partition", ss, es, 0xa5);
addpart(ss, es-ss, 0xa5);
i = es-1;
continue;
}
}
}
}
outparts();
exit(0);
}