bug-grub
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: How To Write Extended Partition Tables from GRUB?


From: sburtchin
Subject: Re: How To Write Extended Partition Tables from GRUB?
Date: Tue, 28 Nov 2006 02:54:57 -0800 (PST)


sburtchin wrote:
> 
> . . . So a slightly modified "parttype" (lets call it "EPTpartnew") could
> be made to write a longer string to anywhere within the same sector
> without too much modification. . . . A user friendly "EPTpartnew" would be
> nice (probably a good bit of work), . . . I can compile it with Linux to
> create the binaries myself.  Can you explain the steps to do this? . . . 
> accept a longer input string and write a longer output string with
> different offset.
> 
After a quick C refresher, I created the new GRUB command myself based on
the "0.97" release.  I decided that "eptedit" would be a more appropriate
name.  Please tell me how I can get this added to the official GRUB source. 
Code follows.


First, a quick summary of situations where this command might be useful:

When installing a mix of old and new operating systems, there are good
reasons for not wanting all to see the same extended partition.  The
operating systems that rely on CHS addressing cannot see past cylinder 1023,
and indeed this can lead to data corruption if they are allowed to see a
partition that straddles this limit.  This situation is illustrated by the
graphic in my previous post.  The solution is to create two alternate
definitions for the extended partition in the MBR with same starting sector,
but different ending sectors ("partnew" can be used to alternate between
them).  The new "eptedit" command can be used to blank the "next" entry in
the last logical partition of the shorter extended partition.  When
returning to the longer extended partition, "eptedit" can be used to restore
the "next" entry.

Another situation is in dealing with the DOS/Win9x "last logical partition"
bug.  These os's must see the last logical partition as FAT/FAT16/FAT32 or
data corruption could result.  If you wish to format the last logical
partition as something else, then the same solution can be applied such that
DOS/Win9x see a shorter extended partition ending in one of these recognized
file systems.

Another situation deals with data recovery.  If the partition tables happen
to become corrupted, fixing these errors can be the first and best step to
data recovery.  There are tools for doing this, but a much quicker approach
would be to add a "Restore All Partition Tables" selection to the GRUB menu. 
This is easily scripted in "menu.lst" using a combination of "partnew" and
"eptedit" commands.


Now the code for the new "eptedit" command:
/* eptedit PART SLOT TYPE BCYL BHEAD BSEC ECYL EHEAD ESEC RSEC TSEC */
static int
eptedit_func (char *arg, int flags)
{
  int ept_slot, new_type, rel_sec, tot_sec;
  int beg_cyl, beg_head, beg_sec, end_cyl, end_head, end_sec;
  int start_cl, start_ch, start_dh;
  int end_cl, end_ch, end_dh;
  unsigned long part = 0xFFFFFF;
  unsigned long start, len, offset, ext_offset;
  int entry, type;
  char mbr[512];

  /* Convert CHS address to the INT 13 format.  */
  auto void chs_to_int13 (int cylinder, int head, int sector, int *cl, int
*ch, int *dh);
  void chs_to_int13 (int cylinder, int head, int sector, int *cl, int *ch,
int *dh)
    {
      if (cylinder >= buf_geom.cylinders)
        cylinder = buf_geom.cylinders - 1;
      
      *cl = sector | ((cylinder & 0x300) >> 2);
      *ch = cylinder & 0xFF;
      *dh = head;
    }
      
  /* Get the drive and the partition.  */
  if (! set_device (arg))
    return 1;

  /* The drive must be a hard disk.  */
  if (! (current_drive & 0x80))
    {
      errnum = ERR_BAD_ARGUMENT;
      return 1;
    }

  /* The partition must be a PC slice.  */
  if ((current_partition >> 16) == 0xFF
      || (current_partition & 0xFFFF) != 0xFFFF)
    {
      errnum = ERR_BAD_ARGUMENT;
      return 1;
    }

  /* Get the EPT slot. */
  arg = skip_to (0, arg);
  if (! safe_parse_maxint (&arg, &ept_slot))
    return 1;

  /* The EPT slot must be "current" or "next".  */
  if ( !(ept_slot == 'c' || ept_slot == 'n') )
    {
      errnum = ERR_BAD_ARGUMENT;
      return 1;
    }

  /* Get the new partition type.  */
  arg = skip_to (0, arg);
  if (! safe_parse_maxint (&arg, &new_type))
    return 1;

  /* The partition type is unsigned char.  */
  if (new_type > 0xFF)
    {
      errnum = ERR_BAD_ARGUMENT;
      return 1;
    }

  /* Get the new begin cylinder.  */
  arg = skip_to (0, arg);
  if (! safe_parse_maxint (&arg, &beg_cyl))
    return 1;
  
  /* Check for start past end of disk.  */
  if (beg_cyl >= buf_geom.total_sectors / (buf_geom.heads *
buf_geom.sectors) )
    {
      errnum = ERR_GEOM;
      return 1;
    }

  /* Get the new begin head.  */
  arg = skip_to (0, arg);
  if (! safe_parse_maxint (&arg, &beg_head))
    return 1;
  
  /* Check for invalid head value.  */
  if (beg_head >= buf_geom.heads)
    {
      errnum = ERR_GEOM;
      return 1;
    }

  /* Get the new begin sector.  */
  arg = skip_to (0, arg);
  if (! safe_parse_maxint (&arg, &begin_sec))
    return 1;
  
  /* Check for invalid sector value.  */
  if (beg_sec > buf_geom.sectors)
    {
      errnum = ERR_GEOM;
      return 1;
    }

  /* Get the new end cylinder.  */
  arg = skip_to (0, arg);
  if (! safe_parse_maxint (&arg, &end_cyl))
    return 1;
  
  /* Check for end past end of disk.  */
  if (end_cyl >= buf_geom.total_sectors / (buf_geom.heads *
buf_geom.sectors) )
    {
      errnum = ERR_GEOM;
      return 1;
    }

  /* Get the new end head.  */
  arg = skip_to (0, arg);
  if (! safe_parse_maxint (&arg, &end_head))
    return 1;
  
  /* Check for invalid head value.  */
  if (end_head >= buf_geom.heads)
    {
      errnum = ERR_GEOM;
      return 1;
    }

  /* Get the new end sector.  */
  arg = skip_to (0, arg);
  if (! safe_parse_maxint (&arg, &end_sec))
    return 1;
  
  /* Check for invalid sector value.  */
  if (end_sec > buf_geom.sectors)
    {
      errnum = ERR_GEOM;
      return 1;
    }

  /* Get the new relative sectors.  */
  arg = skip_to (0, arg);
  if (! safe_parse_maxint (&arg, &rel_sec))
    return 1;
  
  /* Get the new total sectors.  */
  arg = skip_to (0, arg);
  if (! safe_parse_maxint (&arg, &tot_sec))
    return 1;

  /* Check for unreasonable relative sectors + total sectors.  */
  if (rel_sec + tot_sec > buf_geom.total_sectors)
    {
      errnum = ERR_GEOM;
      return 1;
    }

  /* Store the partition information in the MBR.  */
  chs_to_int13 (beg_cyl, beg_head, beg_sec, &start_cl, &start_ch,
&start_dh);
  chs_to_int13 (end_cyl, end_head, end_sec, &end_cl, &end_ch, &end_dh);

  /* Look for the partition.  */
  while (next_partition (current_drive, 0xFFFFFF, &part, &type,
                         &start, &len, &offset, &entry,
                         &ext_offset, mbr))
    {
      if (part == current_partition)
        {
          /* Found.  */

              /* Adjust for "current" or "next" slot.  */
              if (ept_slot == 'n') entry++;

          /* Set the new values for SLOT in EPT.  */
              PC_SLICE_FLAG (mbr, entry) = 0;
              PC_SLICE_HEAD (mbr, entry) = start_dh;
              PC_SLICE_SEC (mbr, entry) = start_cl;
              PC_SLICE_CYL (mbr, entry) = start_ch;
              PC_SLICE_TYPE (mbr, entry) = new_type;
              PC_SLICE_EHEAD (mbr, entry) = end_dh;
              PC_SLICE_ESEC (mbr, entry) = end_cl;
              PC_SLICE_ECYL (mbr, entry) = end_ch;
              PC_SLICE_START (mbr, entry) = rel_sec;
              PC_SLICE_LENGTH (mbr, entry) = tot_sec;

          
          /* Write back the MBR to the disk.  */
          buf_track = -1;
          if (! rawwrite (current_drive, offset, mbr))
            return 1;

          /* Succeed.  */
          return 0;
        }
    }

  /* The partition was not found.  ERRNUM was set by next_partition.  */
  return 1;
}

static struct builtin builtin_eptedit =
{
  "eptedit",
  eptedit_func,
  BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST,
  "eptedit PART SLOT TYPE BCYL BHEAD BSEC ECYL EHEAD ESEC RSEC TSEC",
  "Change data in SLOT (""c"" [current] or ""n"" [next]) in extended
partition table"
  "of logical partition PART to type TYPE, begin cylinder BCYL, begin head" 
  "BHEAD, begin sector BSEC, end cylinder ECYL, end head EHEAD, end"
  "sector ESEC, relative sectors RSEC, and total sectors TSEC"
};

Please verify that I have made the right assumptions at two points in this
code.  In the lines:
  /* Get the EPT slot. */
  arg = skip_to (0, arg);
  if (! safe_parse_maxint (&arg, &ept_slot))
    return 1;
Does "safe_parse_maxint" convert the arguments to lowercase?  If not, then
the argument validation following would have to allow for uppercase also. 
In the lines:
              /* Adjust for "current" or "next" slot.  */
              if (ept_slot == 'n') entry++;
Have I made the right assumption of how "entry" is used?



Continuing, add following pointer to (builtin_table) in builtins.c:
  &builtin_eptedit,



In ChangeLog add:
2006-11-##  ?name?  <address@hidden>

        Added new command "eptedit" based on code
             derived from functions “partnew” and "parttype",
             based on the patch by ?name? <address@hidden>:
        * stage2/builtins.c
             (eptedit_func): New function.
        (builtin_eptedit): New variable.
        (builtin_table): Added pointer to BUILTIN_EPTEDIT.

Who is responsible for updating the change log?  Please help me fill in the
blanks.



In grub.texi add as illustrated:
.
.
.

@menu
* bootp::                       Initialize a network device via BOOTP
.
.
.
* eptedit::                     Change data in an extended partition table
.
.
.

@node eptedit
@subsection eptedit

@deffn Command eptedit part slot type bcyl bhead bsec ecyl ehead esec rsec
tsec
Change data in an extended partition table. @var{part} is a partition
specification in GRUB syntax (@pxref{Naming convention}); @var{slot}
is the position in the extended partition table and must be "c" [current]
or "n" [next]; @var{type} is the partition type and must be a number in the
range @code{0-0xff}; @var{bcyl} is the beginning cylinder; @var{bhead}
is the beginning head; @var{bsec} is the beginning sector; @var{ecyl}
is the ending cylinder; @var{ehead} is the ending head; @var{esec}
is the ending sector; @var{rsec} is the relative sectors and @var{tsec}
is the total sectors, both in sector units.
@end deffn
.
.
.

Please check this.



In grub.info add:
.
.
.
13.2 The list of general commands
=================================

Commands usable anywhere in the menu and in the command-line.

* Menu:

* bootp::                       Initialize a network device via BOOTP
.
.
.
* eptedit::                     Change data in an extended partition table
.
.
.

File: grub.info,  Node: eptedit,  Next: hide,  Prev: dhcp,  Up: General
commands

13.2.5 eptedit
--------------

 -- Command: eptedit part slot type bcyl bhead bsec ecyl ehead esec rsec
tsec
     Change data in an extended partition table.  PART is a partition
     specification in GRUB syntax (*note Naming convention::); SLOT is
     the position in the extended partition table ("c" [current] or "n"
[next]);
     TYPE is the partition type and must be a number in the range `0-0xff';
     BCYL is the beginning cylinder; BHEAD is the beginning head; BSEC
     is the beginning sector; ECYL is the ending cylinder; EHEAD is the
     ending head; ESEC is the ending sector; RSEC is the relative sectors
     and TSEC is the total sectors, both in sector units.  This is useful
when
     using extended partitions of different ranges to accommodate a mix of
     “LBA aware” and ”LBA ignorant” operating systems, and to avoid the
     “DOS/Win9x last logical partition bug” if you want to have a non-FAT
     last logical partition.
.
.
.

File: grub.info,  Node: Index,  Prev: Internals,  Up: Top

Index
*****

 _[index _]
* Menu:

* blocklist:                             blocklist.            (line  7)
.
.
.
* eptedit:                               eptedit.              (line  7)
.
.
.

Tag Table:
.
.
.
Node: eptedit##### - ?
.
.

Please check this closely, especially the tag table entry.



Is there anything else that needs to be done to create a new command? 
Please let me know.  I will help anyway I can.

Thanks!
-- 
View this message in context: 
http://www.nabble.com/How-To-Write-Extended-Partition-Tables-from-GRUB--tf2594470.html#a7577078
Sent from the Grub - Bugs mailing list archive at Nabble.com.





reply via email to

[Prev in Thread] Current Thread [Next in Thread]