summaryrefslogtreecommitdiffstats
path: root/debian/grub-extras/ntldr-img/utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'debian/grub-extras/ntldr-img/utils.c')
-rw-r--r--debian/grub-extras/ntldr-img/utils.c392
1 files changed, 392 insertions, 0 deletions
diff --git a/debian/grub-extras/ntldr-img/utils.c b/debian/grub-extras/ntldr-img/utils.c
new file mode 100644
index 0000000..d1f3bfa
--- /dev/null
+++ b/debian/grub-extras/ntldr-img/utils.c
@@ -0,0 +1,392 @@
+/*
+ * 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;
+}