/*
    c h g r o o t . c

    DOS program to change root device in Linux bootimage file/disk
    Borland C++ 3.0   1 Sep 92

    Sakari Aaltonen
    Email: sakaria@vipunen.hut.fi
*/


#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <mem.h>
#include <bios.h>

#define OFFSET 508
#define SECTLEN 512

struct
{ char majorname[4];
  int majornum;
  int minormin;
  int minormax;
  int abdiff;
} rootdevice[] =
{ { "fd",  2,  0,  1,  0 },   /* floppy */
  { "hd",  3,  1,  8, 64 },   /* ATID */
  { "sd",  8,  1,  8, 16 }    /* SCSI */
};
int maxdevice = sizeof(rootdevice)/sizeof(rootdevice[0]);

FILE *fimage;      /* if this is not global, something breaks... */

int readsector1(char *, unsigned char *);
void writesector1(char *, unsigned char *, int, int, int);
void errexit(char *, ...);


/*  m a i n   ----------------------------------------------------*/
void main(int argc, char *argv[])
{  int i, iab, idev, times, minor, major,
       newminor, newmajor, imin, imax,
       update, drive;
   unsigned char buffer[SECTLEN], devname[16], stemp[16];

   if(argc!=2)
     errexit( "  usage: chgroot bootimage_filename | -a | -b" );

   /* s h o w  current major,minor */
   drive = readsector1(argv[1], buffer);
   major = *(buffer+1 + OFFSET);
   minor = *(buffer   + OFFSET);
   if(!(major|minor))
     strcpy(devname, "same as boot");       /* root=boot */
   else
   { for(idev = 0; idev<maxdevice; idev++)  /* else find device */
     { if(major!=rootdevice[idev].majornum)
	 continue;
       imin = rootdevice[idev].minormin;
       if(minor<imin)
	 continue;
       imax = rootdevice[idev].minormax;
       iab = rootdevice[idev].abdiff;
       if(minor<=imax)
	 break;
       if(minor-iab>=imin && minor-iab<=imax)
	 break;
     }
     if(idev>=maxdevice)                     /* not found in table */
       errexit("Funny major-minor combination %d-%d (wrong file/disk?)",
					  major, minor);
     strcpy(devname, rootdevice[idev].majorname);
     if(!iab)
       sprintf(stemp, "%d", minor);          /* floppy */
     else if(minor<=imax)
       sprintf(stemp, "a%d", minor);         /* .da */
     else
       sprintf(stemp, "b%d", minor-iab);     /* .db */
     strcat(devname, stemp);
   }
   printf("\nCurrent root partition is %s.\n", devname);


   /* g e t  new major,minor */
   newmajor = major, newminor = minor;
   update = 0;                               /* default no update */
   for(times = 0; times<8; times++)
   { printf( "New root partition (0/fd0/fd1/hdaX/hdbX/sdaX/sdbX): ");
     if(!(fgets(devname, 6, stdin)))
       break;                                /* EOF */
     if(*devname=='0')
     { newmajor = newminor = 0;              /* root=boot */
       update = 1;
       break;
     }
     strlwr(devname);
     for(idev = 0; idev<maxdevice; idev++)   /* find major, minor */
     { if(memcmp(rootdevice[idev].majorname, devname, 2))
	 continue;
       iab = rootdevice[idev].abdiff;
       if(!iab)
	 newminor = atoi(devname+2);         /* floppy */
       else
       { i = *(devname+2);
	 if(!(i=='a' || i=='b'))
	   continue;
	 newminor = atoi(devname+3);
       }
       if(    newminor<rootdevice[idev].minormin
	   || newminor>rootdevice[idev].minormax)
	 continue;
       if(iab && i=='b')
	 newminor += iab;
       newmajor = rootdevice[idev].majornum;
       break;
     }
     if(idev<maxdevice)                      /* found in table */
     { update = 1;
       break;
     }
   }

   /* u p d a t e  new major,minor */
   if(update && (newmajor!=major || newminor!=minor))
     writesector1(argv[1], buffer, drive, newmajor, newminor);
}



/*  r e a d s e c t o r 1 -------------------------------------------*/
int readsector1(char *arg, unsigned char *buf)
{  int i, drive = -1;

   if(*arg=='-')                             /* RAWRITE'd floppy */
   { drive = tolower(*(arg+1))-'a';
     if(drive<0 || drive>1)
       errexit("Funny floppy drive specified");
     if(i = biosdisk(2, drive, 0, 0, 1, 1, buf))
       errexit("Error reading floppy, code %02X", i);
   }
   else                                      /* DOS file */
   { if(!(fimage = fopen(arg, "rb+")))
       errexit("Can't open %s", arg);
     if((i = fread(buf, SECTLEN, 1, fimage))!=1)
       errexit("Error reading %s", arg);
   }
   return drive;
}



/*  w r i t e s e c t o r 1 -------------------------------------------*/
void writesector1(char *filename, unsigned char *buf,
	      int drive, int major, int minor)
{  int i;

   printf("Updating major %d, minor %d...", major, minor);
   *(buf+1+OFFSET) = major;
   *(buf  +OFFSET) = minor;
   if(drive<0)                               /* DOS file */
   { rewind(fimage);
     if((i = fwrite(buf, SECTLEN, 1, fimage))!=1)
       errexit("Error writing %s", filename);
     fclose(fimage);
   }
   else                                      /* RAWRITE'd floppy */
   { if(i = biosdisk(3, drive, 0, 0, 1, 1, buf))
       errexit("Error writing floppy, code %02X", i);
     if(i = biosdisk(4, drive, 0, 0, 1, 1, buf))
       errexit("Error verifying floppy, code %02X", i);
   }
   printf("Done.");
}



/*  e r r e x i t -------------------------------------------*/
void errexit(char *fmt, ...)
{  va_list args;
   char text[80];

   va_start(args, fmt);
   vsprintf(text, fmt, args);
   va_end(args);
   printf("%s\n", text);
   exit(1);
}

