/* * $Id$ * * Copyright (C) 2003-2013 JNode.org * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library 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 Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; If not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ package org.jnode.fs.jfat.command; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; import org.apache.log4j.Logger; import org.jnode.driver.block.BlockDeviceAPI; import org.jnode.driver.bus.ide.IDEConstants; import org.jnode.util.FileUtils; import org.jnode.util.LittleEndian; /** * The MBRFormatter is the main class for writing the stage1 and stage1.5 to the * MBR. * * @author tango */ class Stage1_5 { private static final Logger log = Logger.getLogger(Stage1_5.class); private byte[] stage1_5; static final String GRUB_HOME = "/devices/sg0/boot/grub/"; /** * The Source path for the Grub in CD://devices/sg0/boot/grub/STAGE1. * Because the grub can installed from the Live Boot CD. */ private final String stageResourceName2 = GRUB_HOME + "fat.s15"; private static int INSTALL_PARTITION = 0xFFFFFF; private String configFile; // The Embedded Variables values in Jnode private final int SAVED_ENTRY_NUMBER = 0xe; private final String CONFIG_FILE_NAME = "/boot/grub/menu.lst"; /** * The Method for reading the stage1.5 from the Rescue Disk. * * @param stage2ResourceName * @return * @throws java.io.IOException */ private byte[] getStage1_5(String stage2ResourceName) throws GrubException { if (stage1_5 == null) { byte[] buf; try { File file = new File(stage2ResourceName); InputStream is = new FileInputStream(file); buf = new byte[(int) file.length()]; FileUtils.copy(is, buf); is.close(); } catch (IOException e) { throw new GrubException("error while reading stage 1.5", e); } stage1_5 = buf; } return stage1_5; } /** * The method that will write the stage1.5 for the File System specific to * the Boot-sector to the block device. * * @throws GrubException */ private final void writeStage1_5(long stage1_5_start, ByteBuffer stage1_5, BlockDeviceAPI devApi) throws GrubException { try { devApi.write(stage1_5_start, (stage1_5)); } catch (IOException e) { throw new GrubException("error while writing stage 1.5", e); } } public void format(BlockDeviceAPI devApi, int partitionNumber) throws GrubException { System.out.println("Installation of Stage1.5"); stage1_5 = getStage1_5(stageResourceName2); int size = stage1_5.length / IDEConstants.SECTOR_SIZE; log.info("The Size of the stage1_5 is : " + size); /* * The most important stage of the GRUB BOOTING. THE stage1.5. * * The Embedded variables for the grub setting into the JNode's grub * stage1.5 */ /* * The Blocklists for JNode grub installer is setting to the (512-4)th * position of the Sector1 of the Stage1.5. Blocklists is the size of * the stage1.5 in the sectors unit. */ setLittleEnd_BlockLists(stage1_5, size); /* Fixup the install partition */ setLittleEnd_InstallPartition(stage1_5, INSTALL_PARTITION); setConfigFile(CONFIG_FILE_NAME); /* The Saved Entry Number * */ setLittleEnd_EntryNumber(stage1_5, SAVED_ENTRY_NUMBER); /* * The most important section of the Grub The path of the stage2 in the * stage1.5 * * NOTE: Here at the ox19 offset of the second Sector of the stage1.5. * the value of the Drive Path is kept where the stage2 is kept. Ex: as * here the /dev/hd0 is used (ie the partition where the FATfs is kept * and where the stage2 will keep. So here the value set as 0x00. * * The path of the stage2 is very important.Otherwise it will can create * ERROR 17. * * Suppose (hd0,1)/boot/grub/stage2-- (hd0,1) corresponds to linux * partition /dev/hda2 (or /dev/sda2, depending on bios).So hd0 is the * first hard disk found by bios. The "1" stands for partition number * starting from "0". Under linux partition numbers start with 1. * Therefore, the number differs.When this path is patched into stage1.5 * at position 512+0x12+5, then the device specification (hd0,1) is * converted to binary, e.g. 0x8001ffff (0x80 first hard disk, 0x01 * first partition, 0xffff only for BSD partition).The directory * /boot/grub/stage2 is relative to the partition, so if you have a * /boot partition, then the path would be just /grub/stage2. Normally * grub should detect the mapping of unix partition to its own partition * numbering scheme automatically. In some cases this does not work, * e.g. if you have multiple hard disks, the numbering of your BIOS is * hard to predict. Grub uses a file device.map where you can change the * numbering manually. * * * <b>BUGS:</b>1) As currently it is only statically written here the * value of the 0x00; so the stage2 is need to only kept at the * /devices/hdb0.For supporting it in the any partition here need to * change once little bit logic. * * 2)Need to support the Device.map for MUltiple Disk supporting in the * JNODE. */ setLittleEnd_DrivePath(stage1_5, partitionNumber); /* * Fixup the config file TODO: here to be change that the Config File * will write after skipping the /boot/grub/stage2 */ if (configFile != null) { int ofs = 512 + 0x27; while (stage1_5[ofs] != 0) { ofs++; } ofs++; /* Skip '\0' */ for (int i = 0; i < configFile.length(); i++) { stage1_5[ofs++] = (byte) configFile.charAt(i); } stage1_5[ofs] = 0; } /* * The Method for writing the Stage1.5 to the Sector 1 actually to the * second sector. */ writeStage1_5(IDEConstants.SECTOR_SIZE, ByteBuffer.wrap(stage1_5), devApi); System.out.println("Writing stage 1.5 has been completed."); } /** * The Install Partition setting * * @arch i386 * @param stage1_5 * @param installPartition */ private void setLittleEnd_InstallPartition(byte[] stage1_5, int installPartition) { LittleEndian.setInt32(stage1_5, 512 + 0x08, installPartition); } /** * The saved Entry Number setting. * * @arch i386 * @param stage1_5 * @param i */ private void setLittleEnd_EntryNumber(byte[] stage1_5, int i) { LittleEndian.setInt32(stage1_5, 512 + 0xc, i); } /** * The BlockLists if the stage1.5 is setting here. * * @arch:i386 * @param stage1_5 * @param size */ private void setLittleEnd_BlockLists(byte[] stage1_5, int size) { LittleEndian.setInt16(stage1_5, 512 - 4, size); } /** * * Setting the Drive path to the stage1.5.Though it is BUGGY yet. * * @arch i386 * @param stage1_5 * @param i */ private void setLittleEnd_DrivePath(byte[] stage1_5, int i) { LittleEndian.setInt8(stage1_5, 512 + 0x19, i); } /** * The reading method of the Configuration File. * * @return */ public String getConfigFile() { return configFile; } /** * The writing method of the Configuration file to the disk. * * @param configFile */ private void setConfigFile(String configFile) { this.configFile = configFile; } }