private void writeRoot(Mfs mfs) throws Exception { TivoDisk disk = mfs.getBootDisk(); MfsHeader mfsHeader = (disk == null) ? null : disk.getMfsHeader(); if (mfsHeader != null) { PartitionEntry pe = mfsHeader.getParent(); // Utils.printf( System.out, "Writing MFS header on %s: %s", disk.getName(), mfsHeader ); Utils.write(Utils.seekBlock(disk, pe.getStartBlock()), mfsHeader); long roundedSize = Utils.roundDown(pe.getSizeBlocks(), MfsView.VOLUME_SIZE_ROUNDING); Utils.write(Utils.seekBlock(disk, pe.getStartBlock() + roundedSize - 1), mfsHeader); } }
@Override public String toString() { StringBuffer sb = new StringBuffer(getClass().getSimpleName()); sb.append(" {") .append( Utils.printf( "startBlockPrimary=%d\0" + "startBlockBackup=%d\0" + "first=%d\0" + "last=%d\0" + "size=%d\0" + "free=%d\0" + "length=%d\0" + "min=%d\0" + "logStamp=%d\0" + "checksum=0x%08X (valid=%B)\0" + "zero=%d\0" + "num=%d\0" + "type=%s\0" + "next=%s\0" + "extra=%d", descriptorStartBlock, backupStartBlock, dataStartBlocks, dataEndBlock, dataSize, freeDataBlocks, descriptorSize, min, logStamp, checksum, validCrc, zero, num, type, next, (extra == null) ? 0 : extra.length)) .append("}\n") .append("Bitmaps: {"); StringBuffer b = new StringBuffer(); int num = 0; for (Bitmap bmp : bitmaps) b.append(Utils.printf("[%d]: %s", num++, bmp.toString())); sb.append(Utils.printf("ints=%s\0" + "bitmaps=%s", Arrays.asList(ints), b.toString())); return sb.toString(); }
private MfsAdd add(String[] args) { try { add(args[0], (args.length > 1) ? args[1] : null); } catch (Exception e) { Utils.printException(getClass().getSimpleName() + " exception: ", e); log.info(e, ""); } return this; }
private SizeSet calcSizes(long maxFreeBlocks, long chunkSize) throws Exception { long dataSize = chunkSize; SizeSet last = new SizeSet(); log.debug("maxFreeBlocks=%d, chunkSize=%d", maxFreeBlocks, chunkSize); /* Find optimal size set. Logic: assume data size is 1 chunk - it will check for minimum required space calculate descriptor partition size to represent all data blocks check if size set is valid - fits the free space (for first run it will not obviously) if it is not valid - recalculate data size that is guaranteed to fit - i.e. free space minus calculated descriptor partition size note that after descriptor size is recalculated using this new (smaller) data size, it can only be the same or smaller if it is valid - check if it makes sense to continue - stop if 1. data size is less or equal than previous known from valid size set 2. unclaimed space (free - descriptor - data) is less than one data chunk if continue - remember valid size set */ while (true) { long descriptorSize = Zone64.calculateDescriptorSize(dataSize / chunkSize) / AppleDisk.BLOCK_SIZE; // account for backup as well and round up long descriptorPartitionSize = Utils.roundUp(descriptorSize * 2, MfsView.VOLUME_SIZE_ROUNDING); long unclaimedBlocks = maxFreeBlocks - (descriptorPartitionSize + dataSize); if (unclaimedBlocks < 0) { // combined size does not fit into free space if (dataSize <= chunkSize) { log.debug( "unclaimedBlocks=%d, descriptorSize=%d, descriptorPartitionSize=%d, dataSize=%d", unclaimedBlocks, descriptorSize, descriptorPartitionSize, dataSize); throw new Exception( "Free space is to small to accomodate necessary data - extension is impossible"); } } else { // combined size fits - it is a valid size set if (last == null) last = new SizeSet(); else { if (dataSize <= last.data) break; } last.partition = descriptorPartitionSize; last.data = dataSize; last.descriptor = descriptorSize; if (unclaimedBlocks < chunkSize) break; } dataSize = maxFreeBlocks - descriptorPartitionSize; } return last; }
@Override public String toString() { StringBuffer sb = new StringBuffer(getClass().getSimpleName()); sb.append(" {") .append( Utils.printf( "fsid=%d\0" + "length=%d\0" + "type=%s\0" + "name='%s'", getFsid(), getLength(), getType(), getName())) .append('}'); return sb.toString(); }
public InodeEntry readData(DataInput in) throws Exception { if (length != null) { byte[] buf = new byte[getReadSize()]; in.readFully(buf); name = new String(buf); int i = name.indexOf('\0'); if (i >= 0) name = name.substring(0, i); } else { fsid = Utils.getUnsigned(in.readInt()); length = in.readUnsignedByte(); type = InodeType.fromInt(in.readUnsignedByte()); } return this; }
public void add(String bootDisk, String externalDisk) throws Exception { log.debug("bootDisk='%s', externalDisk='%s'", bootDisk, externalDisk); Mfs mfs = new Mfs(true, bootDisk); AppleDisk target = null; if (externalDisk == null) target = findLargestFreeSpace(mfs); else target = createNewDisk(mfs, externalDisk); target = add(mfs, target); writeChanges(mfs, target); if (externalDisk != null) Utils.log( "Added disk external disk '%s'. It will be named in Tivo as device '%s'\n", externalDisk, target.getName()); }
private void writeZone(Mfs mfs, Zone z) throws Exception { MfsView v = mfs.getMfs(); PhysicalAddress a; // Utils.printf( System.out, "Writing zone: %s", z.toString() ); a = Utils.getValidPhysicalAddress(v, z.getDescriptorStartBlock()); Utils.write(Utils.seekBlock(a), z); a = Utils.getValidPhysicalAddress(v, z.getBackupStartBlock()); Utils.write(Utils.seekBlock(a), z); }
// @Override me public DataOutput write(DataOutput out) throws Exception { int total = (this instanceof Zone64) ? Zone64.SIZE : Zone32.SIZE; for (int i = 0; i < num; i++) { out.writeInt(ints[i]); total += Utils.SIZEOF_INT; } for (int i = 0; i < num; i++) { bitmaps[i].write(out); total += bitmaps[i].getReadAheadSize() + bitmaps[i].getReadSize(); } if (extra != null) out.write(extra); else { int extraSize = (int) Utils.blocksToBytes(getDescriptorSize()) - total; while (extraSize-- > 0) out.writeByte(0xAA); } return out; }
// @Override me public Zone readData(DataInput in) throws Exception { int total = 0; ints = new Integer[(int) num]; bitmaps = new Bitmap[(int) num]; for (int i = 0; i < num; i++) ints[i] = in.readInt(); total += num * (Integer.SIZE / 8); for (int i = 0; i < num; i++) { bitmaps[i] = Utils.read(in, Bitmap.class); total += bitmaps[i].getReadAheadSize() + bitmaps[i].getReadSize(); } int extraSize = getReadSize() - total - ((this instanceof Zone64) ? Zone64.SIZE : Zone32.SIZE); if (extraSize > 0) { extra = new byte[extraSize]; in.readFully(extra); } return this; }
private Zone addZone(Mfs mfs, long logicalStartBlock, SizeSet sizes, long chunkSize) throws Exception { if ((mfs == null) || (mfs.getMfs() == null)) throw new Exception("No MFS - MFS has not been initialized"); List<Zone> zones = mfs.getZones(); if ((zones == null) || zones.isEmpty()) throw new Exception("No Zones - MFS has not been initialized"); Zone lastZone = zones.get(zones.size() - 1); int baseFsPointer = lastZone.getInts()[0] - (int) lastZone.getNum() * Utils.SIZEOF_INT + (int) Utils.blocksToBytes(lastZone.getDescriptorSize() + 1) + 160; Zone64 z = new Zone64( logicalStartBlock, // descriptor start logicalStartBlock + sizes.partition, // data start sizes.data, // data size chunkSize, baseFsPointer); z.setType(ZoneType.MEDIA); ZoneHeader lastNext = lastZone.getNext(); /* just put the next pointer indicating the end into the new zone and fill the previous next with pointers to the new zone */ z.setNext(lastNext); lastZone.setNext(z.getHeader()); mfs.addZone(z); return z; }