grub2/debian/grub-extras/ntldr-img/utils.c
Colin Watson 6a8ae39eb5 Add 'debian/grub-extras/' from commit 'f2a079441939eee7251bf141986cdd78946e1d20'
git-subtree-dir: debian/grub-extras
git-subtree-mainline: 955d61226a
git-subtree-split: f2a0794419
2018-02-11 23:23:47 +00:00

393 lines
8.9 KiB
C

/*
* GRUB Utilities -- Utilities for GRUB Legacy, GRUB2 and GRUB for DOS
* Copyright (C) 2007 Bean (bean123@126.com)
*
* This program 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 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifdef LINUX
#define _FILE_OFFSET_BITS 64 // This is required to enable 64-bit off_t
#include <unistd.h>
#endif
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include "utils.h"
static unsigned char ebuf[512];
#if defined(WIN32)
#ifdef __GNUC__ // Mingw or Cygwin
#define u_off_t off64_t
#define u_lseek lseek64
#else
#define u_off_t __int64
#define u_lseek _lseeki64
#endif
#else
#define u_off_t off_t // In FreeBSD, off_t is 64-bit !
#define u_lseek lseek
#endif
int go_sect(int hd,unsigned long sec)
{
// Test if 64-bit seek is supported
if (sizeof(u_off_t)>=8)
{
u_off_t bs,rs;
bs=sec;
bs<<=9;
rs=u_lseek(hd,bs,SEEK_SET);
return (bs!=rs);
}
else
{
unsigned long bs[2];
bs[0]=sec<<9;
bs[1]=sec>>23;
if (bs[1])
return 1;
return (lseek(hd,bs[0],SEEK_SET)!=(off_t)bs[0]);
}
}
// Partition enumerator
// xe->cur is the current partition number, before the first call to xd_enum,
// it should be set to 0xFF
// xe->nxt is the target partition number, if it equals 0xFF, it means enumerate
// all partitions, otherwise, it means jump to the specific partition.
int xd_enum(int hd,xde_t* xe)
{
int nn=512,kk=1,cc;
for (cc=xe->cur;;)
{
if (cc==0xFF)
{
unsigned long pt[4][2];
int i,j,np;
if (go_sect(hd,0))
return 1;
if (read(hd,ebuf,nn)!=nn)
return 1;
if (get16(ebuf,0x1FE)!=0xAA55)
return 1;
np=0;
for (i=0x1BE;i<0x1FE;i+=16)
if (ebuf[i+4])
{
if ((pt[np][1]=get32(ebuf,i+12))==0)
return 1;
pt[np++][0]=get32(ebuf,i+8);
}
if (np==0)
return 1;
// Sort partition table base on start address
for (i=0;i<np-1;i++)
{
int k=i;
for (j=i+1;j<np;j++)
if (pt[k][0]>pt[j][0]) k=j;
if (k!=i)
{
unsigned long tt;
tt=pt[i][0];
pt[i][0]=pt[k][0];
pt[k][0]=tt;
tt=pt[i][1];
pt[i][1]=pt[k][1];
pt[k][1]=tt;
}
}
// Should have space for MBR
if (pt[0][0]==0)
return 1;
// Check for partition overlap
for (i=0;i<np-1;i++)
if (pt[i][0]+pt[i][1]>pt[i+1][0])
return 1;
cc=0;
}
else if (kk)
cc++;
if ((unsigned char)cc>xe->nxt)
return 1;
if (cc<4)
{
if (xe->nxt<4)
{
// Empty partition
if (! ebuf[xe->nxt*16+4+0x1BE])
return 1;
xe->cur=xe->nxt;
xe->dfs=ebuf[xe->nxt*16+4+0x1BE];
xe->bse=get32(ebuf,xe->nxt*16+8+0x1BE);
xe->len=get32(ebuf,xe->nxt*16+12+0x1BE);
return 0;
}
else if (xe->nxt!=0xFF)
cc=4;
else while (cc<4)
{
if (ebuf[cc*16+4+0x1BE])
{
xe->cur=cc;
xe->dfs=ebuf[cc*16+4+0x1BE];
xe->bse=get32(ebuf,cc*16+8+0x1BE);
xe->len=get32(ebuf,cc*16+12+0x1BE);
return 0;
}
cc++;
}
}
if ((cc==4) && (kk))
{
int i;
// Scan for extended partition
for (i=0;i<4;i++)
if ((ebuf[i*16+4+0x1BE]==5) || (ebuf[i*16+4+0x1BE]==0xF)) break;
if (i==4)
return 1;
xe->ebs=xe->bse=get32(ebuf,i*16+8+0x1BE);
}
else
{
// Is end of extended partition chain ?
if (((ebuf[4+0x1CE]!=0x5) && (ebuf[4+0x1CE]!=0xF)) ||
(get32(ebuf,8+0x1CE)==0))
return 1;
xe->bse=xe->ebs+get32(ebuf,8+0x1CE);
}
{
while (1)
{
if (go_sect(hd,xe->bse))
return 1;
if (read(hd,ebuf,nn)!=nn)
return 1;
if (get16(ebuf,0x1FE)!=0xAA55)
return 1;
if ((ebuf[4+0x1BE]==5) || (ebuf[4+0x1BE]==0xF))
{
if (get32(ebuf,8+0x1BE)==0)
return 1;
else
{
xe->bse=xe->ebs+get32(ebuf,8+0x1BE);
continue;
}
}
break;
}
kk=(ebuf[4+0x1BE]!=0);
if ((kk) && ((xe->nxt==0xFF) || (cc==xe->nxt)))
{
xe->cur=cc;
xe->dfs=ebuf[4+0x1BE];
xe->bse+=get32(ebuf,8+0x1BE);
xe->len=get32(ebuf,12+0x1BE);
return 0;
}
}
}
}
#define EXT2_SUPER_MAGIC 0xEF53
int mbr_nhd, mbr_spt;
static void split_chs(unsigned char* chs,unsigned long* c,unsigned long* h,unsigned long* s)
{
*h=chs[0];
*s=(chs[1] & 0x3F)-1;
*c=((unsigned long)(chs[1]>>6))*256+chs[2];
}
static int chk_chs(unsigned long nhd,unsigned long spt,unsigned long lba,unsigned char* chs)
{
unsigned long c,h,s;
split_chs(chs,&c,&h,&s);
if (c==0x3FF)
return ((nhd==h+1) && (spt==s+1));
else
return (c*nhd*spt+h*spt+s==lba);
}
static int chk_mbr(unsigned char* buf)
{
unsigned long nhd,spt,a1,a2,c2,h2,s2;
int i;
i=0x1BE;
while ((i<0x1FE) && (buf[i+4]==0))
i+=16;
if (i>=0x1FE)
return 0;
a1=get32(&buf[i],8);
a2=a1+get32(&buf[i],12)-1;
if (a1>=a2)
return 0;
split_chs(buf+i+5,&c2,&h2,&s2);
if (c2==0x3FF)
{
nhd=h2+1;
spt=s2+1;
if (! chk_chs(nhd,spt,a1,buf+i+1))
return 0;
}
else
{
unsigned long c1,h1,s1;
long n1,n2;
split_chs(buf+i+1,&c1,&h1,&s1);
if ((c1==0x3FF) || (c1>c2))
return 0;
n1=(long)(c1*a2)-(long)(c2*a1)-(long)(c1*s2)+(long)(c2*s1);
n2=(long)(c1*h2)-(long)(c2*h1);
if (n2<0)
{
n2=-n2;
n1=-n1;
}
if ((n2==0) || (n1<=0) || (n1 % n2))
return 0;
spt=(unsigned long)(n1/n2);
if (c2)
{
n1=(long)a2-(long)s2-(long)(h2*spt);
n2=(long)(c2*spt);
if ((n2==0) || (n1<=0) || (n1 % n2))
return 0;
nhd=(unsigned long)(n1/n2);
}
else
nhd=h2+1;
}
if ((nhd==0) || (nhd>255) || (spt==0) || (spt>63))
return 0;
i+=16;
while (i<0x1FE)
{
if (buf[i+4])
{
if ((! chk_chs(nhd,spt,get32(&buf[i],8),buf+i+1)) ||
(! chk_chs(nhd,spt,get32(&buf[i],8)+get32(&buf[i],12)-1,buf+i+5)))
return 0;
}
i+=16;
}
mbr_nhd=(int)nhd;
mbr_spt=(int)spt;
return 1;
}
int get_fstype(unsigned char* buf)
{
if (chk_mbr(buf))
return FST_MBR;
// The first sector of EXT2 might not contain the 0xAA55 signature
if (get16(&buf[1024],56)==EXT2_SUPER_MAGIC)
return FST_EXT2;
if (get16(&buf[0],0x1FE)!=0xAA55)
return FST_OTHER;
if (! memcmp(&buf[0x36],"FAT",3))
return ((buf[0x26]==0x28) || (buf[0x26]==0x29))?FST_FAT16:FST_OTHER;
if (! memcmp(&buf[0x52],"FAT32",5))
return ((buf[0x42]==0x28) || (buf[0x42]==0x29))?FST_FAT32:FST_OTHER;
if (! memcmp(&buf[0x3],"NTFS",4))
return ((buf[0]==0xEB) && (buf[1]==0x52))?FST_NTFS:FST_OTHER;
return FST_OTHER;
}
const char* fst2str(int fs)
{
switch (fs) {
case FST_OTHER:
return "Other";
case FST_MBR:
return "MBR";
case FST_FAT16:
return "FAT12/FAT16";
case FST_FAT32:
return "FAT32";
case FST_NTFS:
return "NTFS";
case FST_EXT2:
return "EXT2/EXT3";
default:
return "Unknown";
}
}
typedef struct {
int id;
const char* str;
} fstab_t;
static fstab_t fstab[]= {
{0x1,"FAT12"},
{0x4,"FAT16"},
{0x5,"Extended"},
{0x6,"FAT16B"},
{0x7,"NTFS"},
{0xB,"FAT32"},
{0xC,"FAT32X"},
{0xE,"FAT16X"},
{0xF,"ExtendedX"},
{0x11,"(H)FAT12"},
{0x14,"(H)FAT16"},
{0x16,"(H)FAT16B"},
{0x17,"(H)NTFS"},
{0x1B,"(H)FAT32"},
{0x1C,"(H)FAT32X"},
{0x1E,"(H)FAT16X"},
{0x82,"Swap"},
{0x83,"Ext2"},
{0xA5,"FBSD"},
{0,"Other"}};
const char* dfs2str(int fs)
{
int i;
for (i=0;fstab[i].id;i++)
if (fs==fstab[i].id)
return fstab[i].str;
return fstab[i].str;
}