/* * $Id$ * * Copyright (C) 2003-2015 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.build; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; import java.util.ArrayList; import org.apache.tools.ant.DirectoryScanner; import org.apache.tools.ant.Task; import org.apache.tools.ant.types.FileSet; import org.jnode.driver.ApiNotFoundException; import org.jnode.driver.Device; import org.jnode.driver.DriverException; import org.jnode.driver.block.BlockDeviceAPI; import org.jnode.driver.block.FileDevice; import org.jnode.fs.FSDirectory; import org.jnode.fs.FSEntry; import org.jnode.fs.FSFile; import org.jnode.fs.FileSystemException; import org.jnode.fs.fat.FatFileSystem; import org.jnode.fs.fat.FatFileSystemType; import org.jnode.fs.fat.GrubFatFormatter; import org.jnode.util.FileUtils; /** * Builder for the boot floppy (in fact, it's not a floppy but a cdrom image in the iso format). * * @author epr */ public class BootFloppyBuilder extends Task { private File destFile; private String stage1ResourceName; private String stage2ResourceName; private ArrayList<FileSet> fileSets = new ArrayList<FileSet>(); /** * Build the boot floppy. * * @throws BuildException */ public void execute() throws BuildException { try { if (isExecuteNeeded()) { createImage(); } } catch (Throwable ex) { ex.printStackTrace(System.err); throw new BuildException(ex); } } protected boolean isExecuteNeeded() { final long lmDest = destFile.lastModified(); return (getLastModified() > lmDest); } /** * Create the actual bootfloppy. * * @throws IOException * @throws DriverException * @throws FileSystemException */ public void createImage() throws IOException, DriverException, FileSystemException { final FileDevice newFd = new FileDevice(destFile, "rw"); try { newFd.setLength(getDeviceLength()); formatDevice(newFd); final Device sysDev = getSystemDevice(newFd); final BlockDeviceAPI sysDevApi = sysDev.getAPI(BlockDeviceAPI.class); copySystemFiles(sysDev); sysDevApi.flush(); } catch (ApiNotFoundException ex) { final IOException ioe = new IOException("BlockDeviceAPI not found on device"); ioe.initCause(ex); throw ioe; } finally { newFd.close(); } } /** * Format the given device. * * @param dev * @throws IOException */ protected void formatDevice(Device dev) throws IOException { GrubFatFormatter ff = createFormatter(); try { ff.format(dev.getAPI(BlockDeviceAPI.class)); } catch (ApiNotFoundException ex) { final IOException ioe = new IOException("BlockDeviceAPI not found on device"); ioe.initCause(ex); throw ioe; } } /** * Gets the device the system files must be copied onto. This enabled a * disk to be formatted with partitions. * * @param rootDevice * @return BlockDevice */ protected Device getSystemDevice(Device rootDevice) { return rootDevice; } /** * Copy the system files to the given device. * * @param device * @throws IOException * @throws FileSystemException */ protected void copySystemFiles(Device device) throws IOException, FileSystemException { final FatFileSystem fs = new FatFileSystem(device, false, new FatFileSystemType()); for (FileSet fset : fileSets) { processFileSet(fs, fset); } fs.close(); } private void processFileSet(FatFileSystem fs, FileSet fset) throws IOException { final DirectoryScanner ds = fset.getDirectoryScanner(getProject()); final String[] dirs = ds.getIncludedDirectories(); for (int i = 0; i < dirs.length; i++) { getOrCreateDir(fs, dirs[i]); } final String[] files = ds.getIncludedFiles(); for (int i = 0; i < files.length; i++) { final String fn = files[i]; final int idx = fn.lastIndexOf(File.separatorChar); final FSDirectory dir; final String name; if (idx >= 0) { dir = getOrCreateDir(fs, fn.substring(0, idx)); name = fn.substring(idx + 1); } else { dir = getOrCreateDir(fs, ""); name = fn; } final File f = new File(ds.getBasedir(), fn); addFile(dir, f, name); } } /** * Gets the latest modification date for all of the parameter fileSets. * * @return the latest modification date. */ protected long getLastModified() { long lm = 0l; for (FileSet fset : fileSets) { lm = Math.max(lm, getLastModified(fset)); } return lm; } private long getLastModified(FileSet fset) { final DirectoryScanner ds = fset.getDirectoryScanner(getProject()); final File baseDir = ds.getBasedir(); long lm = 0l; final String[] dirs = ds.getIncludedDirectories(); for (int i = 0; i < dirs.length; i++) { lm = Math.max(lm, new File(baseDir, dirs[i]).lastModified()); } final String[] files = ds.getIncludedFiles(); for (int i = 0; i < files.length; i++) { lm = Math.max(lm, new File(baseDir, files[i]).lastModified()); } return lm; } private FSDirectory getOrCreateDir(FatFileSystem fs, String dirName) throws IOException { FSDirectory dir = fs.getRootDir(); while (dirName.length() > 0) { final int idx = dirName.indexOf(File.separatorChar); final String part; if (idx >= 0) { part = dirName.substring(0, idx); dirName = dirName.substring(idx + 1); } else { part = dirName; dirName = ""; } FSEntry entry; try { entry = dir.getEntry(part); } catch (IOException ex) { // Ignore entry = null; } if (entry == null) { entry = dir.addDirectory(part); } dir = entry.getDirectory(); } return dir; } /** * Add a given file to a given directory with a given filename. * * @param dir * @param src * @param fname * @throws IOException */ private void addFile(FSDirectory dir, File src, String fname) throws IOException { long size = src.length(); /* * log.info( "Adding " + src + " as " + fname + " size " + (size / * 1024) + "Kb"); */ //final byte[] buf = new byte[ (int) size]; final ByteBuffer buf = ByteBuffer.allocate((int) size); InputStream is = new FileInputStream(src); FileUtils.copy(is, buf.array()); is.close(); final FSFile fh = dir.addFile(fname).getFile(); fh.setLength(size); //fh.write(0, buf, 0, buf.length); fh.write(0, buf); log("Added " + src + " as " + fname + " size " + (size / 1024) + "Kb"); } /** * Returns the destFile. * * @return File */ public File getDestFile() { return destFile; } /** * Sets the destFile. * * @param destFile The destFile to set */ public void setDestFile(File destFile) { this.destFile = destFile; } protected GrubFatFormatter createFormatter() throws IOException { return new GrubFatFormatter(0, stage1ResourceName, stage2ResourceName); } protected long getDeviceLength() { return 1440 * 1024; } /** * @return Returns the stage1ResourceName. */ public final String getStage1ResourceName() { return this.stage1ResourceName; } /** * @param stage1ResourceName The stage1ResourceName to set. */ public final void setStage1ResourceName(String stage1ResourceName) { this.stage1ResourceName = stage1ResourceName; } /** * @return Returns the stage2ResourceName. */ public final String getStage2ResourceName() { return this.stage2ResourceName; } /** * @param stage2ResourceName The stage2ResourceName to set. */ public final void setStage2ResourceName(String stage2ResourceName) { this.stage2ResourceName = stage2ResourceName; } /** * Create and add a fileset to this task. * * @return the fileset created */ public FileSet createFileset() { final FileSet fs = new FileSet(); fileSets.add(fs); return fs; } }