package jfparted; import java.util.*; import javaforce.*; /** Contains all static data and partition related functions * * NOTE : This code is shared with jinstall * * "Humor, I Love it, weeeeee" - Data * */ public class Data { public static enum installTypes { LINUX, ALL, CUSTOM }; public static installTypes installType = installTypes.LINUX; public static ArrayList<Device> devices; public static Device guidedTarget; public static ArrayList<String> ops1, ops2; //guided parted operations public static Partition swap, root; public static ArrayList<String> fstab; //filesys table /etc/fstab public static String fullName, loginName, passwd, localhost, localdomain, timezone; public static class Device implements Cloneable { //parted fields public String dev; public String size; public String model; public boolean uninit; public ArrayList<Partition> parts = new ArrayList<Partition>(); public Device clone() { Device clone = new Device(); clone.dev = dev; clone.size = size; clone.model = model; clone.uninit = uninit; clone.parts = new ArrayList<Partition>(); for(int a=0;a<parts.size();a++) { clone.parts.add(parts.get(a).clone()); clone.parts.get(a).device = clone; //make sure to update owner } return clone; } } public static class Partition implements Cloneable { //parted fields public Device device; //owner public int number; public String start, end; public String size; public String type; public String filesys; public String label; //used to format() only public String flags; //extra fields public String mount; public boolean delete; public Partition clone() { Partition n = new Partition(); n.device = device; n.number = number; n.start = start; n.end = end; n.size = size; n.type = type; n.filesys = filesys; n.flags = flags; n.mount = mount; n.delete = delete; return n; } } private static void clearDevices() { devices = new ArrayList<Device>(); } private static void addDevice(String dev, String size, boolean uninit) { Device device = new Device(); device.dev = dev; device.size = size; device.uninit = uninit; devices.add(device); } public static void getDevices() { JFLog.log("getDevices()"); clearDevices(); ShellProcess sp = new ShellProcess(); ArrayList<String> cmd = new ArrayList<String>(); cmd.add("sudo"); cmd.add("parted"); sp.addResponse("(parted) ", "print devices\n", false); sp.addResponse("(parted) ", "quit\n", false); String output = sp.run(cmd, true); if (output == null) {JFLog.log("Failed to exec parted"); return;} String lns[] = output.split("\n"); int i1,i2,i3; String str; for(int idx=0;idx < lns.length;idx++) { str = lns[idx]; if (str.indexOf("Error") != -1 && str.indexOf("unrecognised disk label") != -1) { //str=Error: /dev/sda: unrecognised disk label JFLog.log("Warning:Device has no partition table!"); i1 = str.indexOf("/dev"); i2 = str.substring(i1).indexOf(":"); addDevice(str.substring(i1, i1+i2), "unknown", true); continue; } if (str.startsWith("/dev/fd")) continue; //floppies if (str.startsWith("/dev/sr")) continue; //CD-ROMs if (str.startsWith("/dev/zram")) continue; //RAM drives if (str.startsWith("/dev/mapper")) continue; //mapper drives if (str.startsWith("/dev")) { //str=/dev/sda (300GB) i1 = str.indexOf(" "); i2 = str.indexOf("("); i3 = str.indexOf(")"); addDevice(str.substring(0, i1), str.substring(i2+1,i3-1) + "B", false); } } } private static void getPartitions(Device device) { JFLog.log("getPartitions() for " + device.dev); device.parts.clear(); ShellProcess sp = new ShellProcess(); ArrayList<String> cmd = new ArrayList<String>(); cmd.add("sudo"); cmd.add("parted"); sp.addResponse("(parted) ", "select " + device.dev + "\n", false); sp.addResponse("(parted) ", "print free\n", false); sp.addResponse("(parted) ", "quit\n", false); String output = sp.run(cmd, true); if (output == null) {JFLog.log("Failed to exec parted"); return;} String lns[] = output.split("\n"); int idx = 0, iModel, iNumber, iStart, iEnd, iSize, iType, iFileSys, iFlags; String str; String number; while (idx < lns.length) { str = lns[idx]; if (str.indexOf("Error") != -1 && str.indexOf("unrecognised disk label") != -1) { //parted/3.x started placing this warning here instead of 'print devices' (which is better) JFLog.log("Warning:Device has no partition table!"); device.uninit = true; return; } if (str.startsWith("Model")) { iModel = str.lastIndexOf(" "); // i2 = str.indexOf("("); //(scsi) // i3 = str.indexOf(")"); device.model = str.substring(7, iModel); while (lns[idx].length() > 0) idx++; //skip Model, Disk, Sector size, Partition Table, [Disk Flags] idx++; //skip blank line String cols = lns[idx++]; //Number Start End Size Type File system Flags iNumber = cols.indexOf("Number"); iStart = cols.indexOf("Start"); iEnd = cols.indexOf("End"); iSize = cols.indexOf("Size"); iType = cols.indexOf("Type"); iFileSys = cols.indexOf("File system"); if (iType == -1) iType = iFileSys; iFlags = cols.indexOf("Flags"); while (lns[idx].length() > 0) { str = lns[idx] + " "; int len = str.length(); Partition part = new Partition(); part.device = device; device.parts.add(part); number = str.substring(Math.min(iNumber+1, len-1),Math.min(8, len)).trim(); if (number.length() == 0) part.number = -1; else part.number = Integer.valueOf(number); part.start = str.substring(Math.min(iStart, len-1),Math.min(iEnd, len)).trim(); part.end = str.substring(Math.min(iEnd, len-1),Math.min(iSize, len)).trim(); part.size = str.substring(Math.min(iSize, len-1),Math.min(iType, len)).trim(); if (iType == iFileSys) part.type = ""; else part.type = str.substring(Math.min(iType, len-1),Math.min(iFileSys, len)).trim(); part.filesys = str.substring(Math.min(iFileSys, len-1),Math.min(iFlags, len)).trim(); if (part.filesys.startsWith("linux-swap")) part.filesys = "swap"; part.flags = str.substring(Math.min(iFlags, len-1)).trim(); part.mount = ""; idx++; } } else { idx++; } } } public static void getPartitions() { for(int a=0;a<devices.size();a++) { getPartitions(devices.get(a)); } } public static int getDeviceCount() { return devices.size(); } public static Device getDevice(String dev) { for(int a=0;a<devices.size();a++) { if (devices.get(a).dev.equals(dev)) return devices.get(a); } return null; } public static double getSize(String size) { // ##.# k/M/G/T B if (size == null) return -1.0; int len = size.length(); String number = size.substring(0, len-2).trim(); char exp = size.charAt(len-2); switch (exp) { case 'k': case 'K': return Double.valueOf(number) * 1024.0; case 'm': case 'M': return Double.valueOf(number) * 1024.0 * 1024.0; case 'g': case 'G': return Double.valueOf(number) * 1024.0 * 1024.0 * 1024.0; case 't': case 'T': return Double.valueOf(number) * 1024.0 * 1024.0 * 1024.0 * 1024.0; } return 0.0; } public static String makeSize(double size) { if (size < 1024.0 * 1024.0) { return String.format("%4.1fkB", size / 1024.0).trim(); } if (size < 1024.0 * 1024.0 * 1024.0) { return String.format("%4.1fMB", size / (1024.0 * 1024.0)).trim(); } if (size < 1024.0 * 1024.0 * 1024.0 * 1024.0) { return String.format("%4.1fGB", size / (1024.0 * 1024.0 * 1024.0)).trim(); } return String.format("%4.1fTB", size / (1024.0 * 1024.0 * 1024.0 * 1024.0)).trim(); } public static boolean isLogical(Partition part) { //determines if a block is logical (including Free Space) if (part.type.equals("primary")) return false; if (part.type.equals("extended")) return false; if (part.type.equals("logical")) return true; //check if Free Space is within extended part Partition ext = null; for(int a=0;a<part.device.parts.size();a++) { if (part.device.parts.get(a).type.equals("extended")) {ext = part.device.parts.get(a); break;} } if (ext == null) return false; //no extended partition found if (getSize(part.start) >= getSize(ext.end)) return false; if (getSize(part.end) <= getSize(ext.start)) return false; return true; } /** Deletes partitions in list that are marked for deletion (virtually). */ public static void deletePartitions(Device device) { int idx = 0; Partition prev, next; logDevice(device, "deletePartitions() start (virtually)"); while (idx < device.parts.size()) { Partition part = device.parts.get(idx); if (!part.delete) {idx++; continue;} JFLog.log("Deleting part:" + part.device.dev + part.number); if (part.type.equals("extended")) { JFLog.log("Deleting logical parts of extended part"); //delete all logical partitions (which will always follow) int extidx = idx+1; while (extidx < device.parts.size()) { if (isLogical(device.parts.get(extidx))) { device.parts.remove(extidx); } else { extidx++; } } } boolean partLogical = isLogical(part); boolean merged = false; if (idx > 0) { prev = device.parts.get(idx-1); boolean prevLogical = isLogical(prev); if (prev.filesys.equals("Free Space") && (partLogical == prevLogical)) { //join with previous block JFLog.log("Merging part with prev:" + prev.device.dev + prev.number); prev.end = part.end; JFLog.log("prev.size=" + prev.size + ",part.size=" + part.size); prev.size = makeSize(getSize(prev.size) + getSize(part.size)); JFLog.log("merged.size=" + prev.size); device.parts.remove(idx); merged = true; part = prev; partLogical = prevLogical; idx--; } } if (idx+1 < device.parts.size()) { next = device.parts.get(idx+1); boolean nextLogical = isLogical(next); if (next.filesys.equals("Free Space") && (partLogical == nextLogical)) { //join with next block JFLog.log("Merging part with next:" + next.device.dev + next.number); next.start = part.start; JFLog.log("next.size=" + next.size + ",part.size=" + part.size); next.size = makeSize(getSize(next.size) + getSize(part.size)); JFLog.log("merged.size=" + next.size); device.parts.remove(idx); merged = true; } } if (!merged) { JFLog.log("Setting part as free"); part.number = -1; part.type = ""; part.filesys = "Free Space"; idx++; } } logDevice(device, "deletePartitions() done"); } /** Find a place to create a new primary partition. */ public static Partition findPartition(Device device, String reqsizeStr, String filesys) { double reqsize = getSize(reqsizeStr); //NOTE : if sizeStr == null reqsize = -1.0 logDevice(device, "findPartition() start"); //check if there are already 4 pri/ext partitions defined int pecnt = 0; for(int idx=0;idx<device.parts.size();idx++) { if (device.parts.get(idx).type.equals("primary")) pecnt++; if (device.parts.get(idx).type.equals("extended")) pecnt++; } if (pecnt >= 4) { JFLog.log("findPartition() : Device already has 4 primary partitions."); return null; } //NOTE:should NEVER be > 4 int bestidx = -1; double excess = -1.0; double bestsize = 0.0; double partsize; Partition part; for(int idx=0;idx<device.parts.size();idx++) { part = device.parts.get(idx); JFLog.log("part:" + part.device.dev + part.number + "," + part.filesys); if (part.device != device) JFLog.log("Warning:device has changed in partition"); if (!part.filesys.equals("Free Space")) {JFLog.log("not free"); continue;} if (isLogical(part)) {JFLog.log("logical"); continue;} JFLog.log("checking:" + idx); partsize = getSize(part.size); if (reqsize != -1.0) { if (partsize < reqsize) { JFLog.log("too small"); continue; } //too small if (excess == -1.0) { //found a match excess = partsize - reqsize; bestidx = idx; continue; } if (partsize - reqsize < excess) { //found a better match excess = partsize - reqsize; bestidx = idx; continue; } } else { if (partsize > bestsize) { //found a [better] match bestsize = partsize; bestidx = idx; } } } if (bestidx == -1) { JFLog.log("findPartition() : Could not find a match."); return null; } part = device.parts.get(bestidx); if ((reqsize == -1) || (reqsize == getSize(part.size))) { part.number = nextNumber(device); part.filesys = filesys; part.type = "primary"; logDevice(device, "findPartition() done : full"); return part; } //split part into two int number = nextNumber(device); if (number == -1) return null; part.type = ""; part.filesys = "Free Space"; part.size = makeSize(getSize(part.size) - reqsize); Partition newpart = new Partition(); newpart.device = part.device; newpart.end = part.end; part.end = makeSize(getSize(part.end) - reqsize); newpart.number = number; newpart.filesys = filesys; newpart.type = "primary"; newpart.start = part.end; newpart.size = reqsizeStr; device.parts.add(newpart); logDevice(device, "findPartition() done : split"); return newpart; } private static int nextNumber(Device device) { //calc next partition # that would be given by parted boolean used[] = new boolean[4]; Partition part; for(int idx=0;idx<device.parts.size();idx++) { part = device.parts.get(idx); if (part.type.equals("primary")) {used[part.number-1] = true; continue;} if (part.type.equals("extended")) {used[part.number-1] = true; continue;} } for(int idx=0;idx<4;idx++) { if (!used[idx]) return idx+1; } return -1; //ohOh } public static boolean deviceHasExtPart(Device device) { for(int idx=0;idx<device.parts.size();idx++) { if (device.parts.get(idx).type.equals("extended")) return true; } return false; } public static boolean createPartTable(String dev) { //exec "sudo parted" //commands : "select 'dev'" and "mktable msdos" ShellProcess sp = new ShellProcess(); ArrayList<String> cmd = new ArrayList<String>(); cmd.add("sudo"); cmd.add("parted"); sp.addResponse("(parted) ", "select " + dev + "\n", false); sp.addResponse("(parted) ", "mktable msdos\n", false); sp.addResponse("(parted) ", "quit\n", false); String output = sp.run(cmd, true); if (output == null) {JFLog.log("Failed to exec parted"); return false;} return true; } public static boolean createPart(Device device, String type, String filesys, String start, String end) { //exec "sudo parted" //commands : "select $dev" and "mkpart $type [$filesys] start end" ShellProcess sp = new ShellProcess(); ArrayList<String> cmd = new ArrayList<String>(); cmd.add("sudo"); cmd.add("parted"); if ((filesys != null) && (filesys.equals("swap"))) filesys = "linux-swap(v1)"; sp.addResponse("(parted) ", "select " + device.dev + "\n", false); sp.addResponse("(parted) ", "mkpart " + type + " " + (filesys != null ? filesys : "") + " " + start + " " + end + "\n", false); sp.addResponse("(parted) ", "quit\n", false); String output = sp.run(cmd, true); if (output == null) {JFLog.log("Failed to exec parted"); return false;} return true; } public static boolean deletePart(Partition part) { //exec "sudo parted" //commands : "select $part.device.dev" and "rm $part.number" if (part.number == -1) return false; ShellProcess sp = new ShellProcess(); ArrayList<String> cmd = new ArrayList<String>(); cmd.add("sudo"); cmd.add("parted"); sp.addResponse("(parted) ", "select " + part.device.dev + "\n", false); sp.addResponse("(parted) ", "rm " + part.number + "\n", false); sp.addResponse("(parted) ", "quit\n", false); String output = sp.run(cmd, true); if (output == null) {JFLog.log("Failed to exec parted"); return false;} return true; } public static boolean format(Partition part) { return format(part, null); } public static boolean format(Partition part, ShellProcessListener listener) { //exec "sudo mkfs.* $part.device.dev+$part.number" if (part.number == -1) return false; ShellProcess sp = new ShellProcess(); ArrayList<String> cmd = new ArrayList<String>(); cmd.add("sudo"); if (part.filesys.equals("ext4")) {cmd.add("mkfs.ext4"); /*cmd.add("-O"); cmd.add("extend");*/} else if (part.filesys.equals("ext3")) {cmd.add("mkfs.ext3"); /*cmd.add("-O"); cmd.add("extend");*/} else if (part.filesys.equals("ext3")) {cmd.add("mkfs.ext2"); /*cmd.add("-O"); cmd.add("extend");*/} else if (part.filesys.equals("ntfs")) {cmd.add("mkfs.ntfs"); cmd.add("-Q");} else if (part.filesys.equals("swap")) cmd.add("mkswap"); else if (part.filesys.equals("fat32")) {cmd.add("mkfs.vfat"); cmd.add("-F"); cmd.add("32");} else if (part.filesys.equals("fat16")) {cmd.add("mkfs.vfat"); cmd.add("-F"); cmd.add("16");} else { JFLog.log("Error:Unsupported filesys:" + part.filesys); return false; } cmd.add(part.device.dev + part.number); cmd.add("-L"); cmd.add(part.label); if (listener != null) sp.addListener(listener); String output = sp.run(cmd, true); if (output == null) {JFLog.log("Failed to exec " + cmd.get(1)); return false;} return true; } /** Creates a folder with sudo. */ public static boolean mkdir(String folder) { ShellProcess sp = new ShellProcess(); ArrayList<String> cmd = new ArrayList<String>(); cmd.add("sudo"); cmd.add("mkdir"); cmd.add("-p"); cmd.add(folder); String output = sp.run(cmd, true); if (output == null) {JFLog.log("Failed to exec mkdir"); return false;} if (output.length() > 0) { JFLog.log("Error:" + output); return false; } return true; } /** Changes a folder mode bits */ public static boolean chmod(String folder, String mod) { ShellProcess sp = new ShellProcess(); ArrayList<String> cmd = new ArrayList<String>(); cmd.add("sudo"); cmd.add("chmod"); cmd.add(mod); cmd.add(folder); String output = sp.run(cmd, true); if (output == null) {JFLog.log("Failed to exec chmod"); return false;} if (output.length() > 0) { JFLog.log("Error:" + output); return false; } return true; } /** Mount --bind oldFolder newFolder */ public static boolean mount_bind(String oldFolder, String newFolder) { //exec "sudo mkdir -p $folder" //exec "sudo mount --bind $oldFolder $newFolder" if (!mkdir(newFolder)) return false; ShellProcess sp = new ShellProcess(); ArrayList<String> cmd = new ArrayList<String>(); String output; cmd.add("sudo"); cmd.add("mount"); cmd.add("--bind"); cmd.add(newFolder); cmd.add(oldFolder); output = sp.run(cmd, true); if (output == null) {JFLog.log("Failed to exec mount"); return false;} if (output.length() > 0) { JFLog.log("Error:" + output); return false; } return true; } /** Mount a part to a folder. The folder will be created if needed. */ public static boolean mount(Partition part, String folder) { //exec "sudo mkdir -p $folder" //exec "sudo mount $device+$part.number $folder" if (part.number == -1) return false; if (!mkdir(folder)) return false; ShellProcess sp = new ShellProcess(); ArrayList<String> cmd = new ArrayList<String>(); String output; cmd.add("sudo"); cmd.add("mount"); cmd.add(part.device.dev + part.number); cmd.add(folder); output = sp.run(cmd, true); if (output == null) {JFLog.log("Failed to exec mount"); return false;} if (output.length() > 0) { JFLog.log("Error:" + output); return false; } return true; } /** Mount dev to folder */ public static boolean mount(String dev, String folder) { //exec "sudo mkdir -p $folder" //exec "sudo mount --bind $oldFolder $newFolder" if (!mkdir(folder)) return false; ShellProcess sp = new ShellProcess(); ArrayList<String> cmd = new ArrayList<String>(); String output; cmd.add("sudo"); cmd.add("mount"); cmd.add(dev); cmd.add(folder); output = sp.run(cmd, true); if (output == null) {JFLog.log("Failed to exec mount"); return false;} if (output.length() > 0) { JFLog.log("Error:" + output); return false; } return true; } public static boolean umount(String dev) { //exec "sudo mount /mnt/install" ShellProcess sp = new ShellProcess(); ArrayList<String> cmd = new ArrayList<String>(); cmd.add("sudo"); cmd.add("umount"); cmd.add(dev); String output = sp.run(cmd, true); if (output == null) {JFLog.log("Failed to exec umount"); return false;} if (output.length() > 0) { JFLog.log("Error:" + output); return false; } return true; } public static void clearfstab() { fstab = new ArrayList<String>(); } public static void addfstab(String dev, String mount, String filesys, String opts, int dump, int pass) { //<file system> <mount point> <filesys> <options> <dump> <pass> if (mount.equals("swap")) mount = "none"; fstab.add(dev + "\t" + mount + "\t" + filesys + "\t" + opts + "\t" + dump + "\t" + pass); } /** Outputs current partition table to log. */ public static void logDevice(Device device, String msg) { if (msg != null) JFLog.log(msg); JFLog.log("Device:" + device.dev + ":" + device.model); JFLog.log("Part: Number\t Start\t End\t Type\t File system\t Size\t Mount"); for(int p=0;p<device.parts.size();p++) { Partition part = device.parts.get(p); String log = String.format("Part:%8d\t%8s\t%8s\t%8s\t%12s\t%8s\t%8s" , part.number, part.start, part.end, part.type, part.filesys, part.size, part.mount); JFLog.log(log); } } }