Got all the bugs out of the directory handling and also removed a lot of bugs and side-effects through code refactoring

This commit is contained in:
Brendan Robert 2015-12-28 20:58:48 -06:00
parent 59ab31f433
commit 8bcf3a922a
6 changed files with 81 additions and 74 deletions

View File

@ -37,21 +37,23 @@ public class DirectoryNode extends DiskNode implements FileFilter {
// public static int FILE_ENTRY_SIZE = 38; // public static int FILE_ENTRY_SIZE = 38;
public static int FILE_ENTRY_SIZE = 0x027; public static int FILE_ENTRY_SIZE = 0x027;
private boolean isRoot;
public DirectoryNode(ProdosVirtualDisk ownerFilesystem, File physicalDir, int baseBlock) throws IOException {
setBaseBlock(baseBlock); public DirectoryNode(ProdosVirtualDisk ownerFilesystem, File physicalDir, int baseBlock, boolean root) throws IOException {
init(ownerFilesystem, physicalDir); super(ownerFilesystem, baseBlock);
init(ownerFilesystem, physicalDir, root);
} }
public DirectoryNode(ProdosVirtualDisk ownerFilesystem, File physicalDir) throws IOException { public DirectoryNode(ProdosVirtualDisk ownerFilesystem, File physicalDir, boolean root) throws IOException {
init(ownerFilesystem, physicalDir); super(ownerFilesystem);
init(ownerFilesystem, physicalDir, root);
} }
private void init(ProdosVirtualDisk ownerFilesystem, File physicalFile) throws IOException { private void init(ProdosVirtualDisk ownerFilesystem, File physicalFile, boolean root) throws IOException {
isRoot = root;
setPhysicalFile(physicalFile); setPhysicalFile(physicalFile);
setType(EntryType.SUBDIRECTORY); setType(EntryType.SUBDIRECTORY);
setName(physicalFile.getName()); setName(physicalFile.getName());
setOwnerFilesystem(ownerFilesystem);
} }
@Override @Override
@ -59,15 +61,15 @@ public class DirectoryNode extends DiskNode implements FileFilter {
} }
@Override @Override
public void doAllocate() { public void doAllocate() throws IOException {
File[] files = physicalFile.listFiles(this); File[] files = physicalFile.listFiles(this);
int numEntries = files.length; int numEntries = files.length;
int numBlocks = 1;
// First block has 12 entries, subsequent blocks have 13 entries // First block has 12 entries, subsequent blocks have 13 entries
if (numEntries > 12) { int numBlocks = 1 + numEntries / 13;
numBlocks += (numEntries - 12) / 13; for (int i=1; i < numBlocks; i++) {
new SubNode(i, this, getOwnerFilesystem().getNextFreeBlock());
} }
for (File f : files) { for (File f : files) {
addFile(f); addFile(f);
} }
@ -126,6 +128,7 @@ public class DirectoryNode extends DiskNode implements FileFilter {
generateFileEntry(buffer, 4 + (i + 1) * FILE_ENTRY_SIZE, i); generateFileEntry(buffer, 4 + (i + 1) * FILE_ENTRY_SIZE, i);
} }
} else { } else {
generatePointers(buffer, additionalNodes.get(block-1).getBaseBlock(), block < (additionalNodes.size()-1) ? additionalNodes.get(block+1).getBaseBlock() : 0);
int start = (block * 13) - 1; int start = (block * 13) - 1;
int end = start + 13; int end = start + 13;
int offset = 4; int offset = 4;
@ -150,6 +153,13 @@ public class DirectoryNode extends DiskNode implements FileFilter {
return !file.isHidden(); return !file.isHidden();
} }
private void generatePointers(byte[] buffer, int prevBlock, int nextBlock) {
// Previous block = 0
generateWord(buffer, 0, prevBlock);
// Next block
generateWord(buffer, 0x02, nextBlock);
}
/** /**
* Generate the directory header found in the base block of a directory * Generate the directory header found in the base block of a directory
* *
@ -157,21 +167,17 @@ public class DirectoryNode extends DiskNode implements FileFilter {
*/ */
@SuppressWarnings("static-access") @SuppressWarnings("static-access")
private void generateHeader(byte[] buffer) { private void generateHeader(byte[] buffer) {
// Previous block = 0 generatePointers(buffer, 0, additionalNodes.size()>1 ? additionalNodes.get(1).getBaseBlock() : 0);
generateWord(buffer, 0, 0);
// Next block
int nextBlock = 0;
if (!additionalNodes.isEmpty()) {
nextBlock = additionalNodes.get(0).baseBlock;
}
generateWord(buffer, 0x02, nextBlock);
// Directory header + name length // Directory header + name length
// Volumme header = 0x0f0; Subdirectory header = 0x0e0 // Volumme header = 0x0f0; Subdirectory header = 0x0e0
buffer[4] = (byte) ((baseBlock == 0x02 ? 0x0f0 : 0x0E0) + getName().length()); buffer[4] = (byte) ((isRoot ? 0x0F0 : 0x0E0) | getName().length());
generateName(buffer, 5, this); generateName(buffer, 5, this);
for (int i = 0x014; i <= 0x01b; i++) { for (int i = 0x014; i <= 0x01b; i++) {
buffer[i] = 0; buffer[i] = 0;
} }
if (!isRoot) {
buffer[0x014] = 0x075;
}
generateTimestamp(buffer, 0x01c, getPhysicalFile().lastModified()); generateTimestamp(buffer, 0x01c, getPhysicalFile().lastModified());
// Prodos 1.9 // Prodos 1.9
buffer[0x020] = 0x019; buffer[0x020] = 0x019;
@ -185,10 +191,17 @@ public class DirectoryNode extends DiskNode implements FileFilter {
buffer[0x024] = (byte) 0x0d; buffer[0x024] = (byte) 0x0d;
// Directory items count // Directory items count
generateWord(buffer, 0x025, children.size()); generateWord(buffer, 0x025, children.size());
// Volume bitmap pointer if (isRoot) {
generateWord(buffer, 0x027, ownerFilesystem.freespaceBitmap.baseBlock); // Volume bitmap pointer
// Total number of blocks generateWord(buffer, 0x027, ownerFilesystem.freespaceBitmap.getBaseBlock());
generateWord(buffer, 0x029, ownerFilesystem.MAX_BLOCK); // Total number of blocks
generateWord(buffer, 0x029, ownerFilesystem.MAX_BLOCK);
} else {
// Parent pointer
generateWord(buffer, 0x027, getParent().getBaseBlock());
buffer[0x029] = (byte) (getParent().getChildren().indexOf(this) + 1);
buffer[0x02a] = 0x027;
}
} }
/** /**
@ -200,7 +213,6 @@ public class DirectoryNode extends DiskNode implements FileFilter {
*/ */
private void generateFileEntry(byte[] buffer, int offset, int fileNumber) throws IOException { private void generateFileEntry(byte[] buffer, int offset, int fileNumber) throws IOException {
DiskNode child = children.get(fileNumber); DiskNode child = children.get(fileNumber);
child.allocate();
// Entry Type and length // Entry Type and length
buffer[offset] = (byte) ((child.getType().code << 4) + child.getName().length()); buffer[offset] = (byte) ((child.getType().code << 4) + child.getName().length());
// Name // Name
@ -261,7 +273,7 @@ public class DirectoryNode extends DiskNode implements FileFilter {
private void addFile(File file) { private void addFile(File file) {
try { try {
if (file.isDirectory()) { if (file.isDirectory()) {
addChild(new DirectoryNode(getOwnerFilesystem(), file)); addChild(new DirectoryNode(getOwnerFilesystem(), file, false));
} else { } else {
addChild(new FileNode(getOwnerFilesystem(), file)); addChild(new FileNode(getOwnerFilesystem(), file));
} }

View File

@ -33,7 +33,6 @@ import java.util.List;
public abstract class DiskNode { public abstract class DiskNode {
public enum EntryType { public enum EntryType {
DELETED(0), DELETED(0),
SEEDLING(1), SEEDLING(1),
SAPLING(2), SAPLING(2),
@ -50,7 +49,7 @@ public abstract class DiskNode {
boolean allocated = false; boolean allocated = false;
long allocationTime = -1L; long allocationTime = -1L;
long lastCheckTime = -1L; long lastCheckTime = -1L;
int baseBlock = -1; private int baseBlock = -1;
List<DiskNode> additionalNodes; List<DiskNode> additionalNodes;
ProdosVirtualDisk ownerFilesystem; ProdosVirtualDisk ownerFilesystem;
File physicalFile; File physicalFile;
@ -59,13 +58,23 @@ public abstract class DiskNode {
private EntryType type; private EntryType type;
private String name; private String name;
public DiskNode() { public DiskNode(ProdosVirtualDisk fs) throws IOException {
init(fs);
setBaseBlock(fs.getNextFreeBlock());
}
public DiskNode(ProdosVirtualDisk fs, int blockNumber) throws IOException {
init(fs);
setBaseBlock(blockNumber);
}
private void init(ProdosVirtualDisk fs) throws IOException {
additionalNodes = new ArrayList<>(); additionalNodes = new ArrayList<>();
children = new ArrayList<>(); children = new ArrayList<>();
setOwnerFilesystem(fs);
} }
public boolean checkFile() throws IOException { public boolean checkFile() throws IOException {
allocate();
if (physicalFile == null) { if (physicalFile == null) {
return false; return false;
} }
@ -80,9 +89,9 @@ public abstract class DiskNode {
public void allocate() throws IOException { public void allocate() throws IOException {
if (!allocated) { if (!allocated) {
doAllocate(); doAllocate();
getOwnerFilesystem().physicalMap.put(baseBlock, this);
allocationTime = System.currentTimeMillis(); allocationTime = System.currentTimeMillis();
allocated = true; allocated = true;
ownerFilesystem.allocateEntry(this);
} }
} }
@ -101,11 +110,9 @@ public abstract class DiskNode {
} }
public void refresh() throws IOException { public void refresh() throws IOException {
ownerFilesystem.deallocateEntry(this); deallocate();
doRefresh(); doRefresh();
allocationTime = System.currentTimeMillis(); allocate();
allocated = true;
ownerFilesystem.allocateEntry(this);
} }
/** /**
@ -139,8 +146,9 @@ public abstract class DiskNode {
/** /**
* @param baseBlock the baseBlock to set * @param baseBlock the baseBlock to set
*/ */
public void setBaseBlock(int baseBlock) { private void setBaseBlock(int baseBlock) {
this.baseBlock = baseBlock; this.baseBlock = baseBlock;
ownerFilesystem.physicalMap.put(baseBlock, this);
} }
/** /**
@ -154,12 +162,8 @@ public abstract class DiskNode {
* @param ownerFilesystem the ownerFilesystem to set * @param ownerFilesystem the ownerFilesystem to set
* @throws IOException * @throws IOException
*/ */
public void setOwnerFilesystem(ProdosVirtualDisk ownerFilesystem) throws IOException { private void setOwnerFilesystem(ProdosVirtualDisk ownerFilesystem) throws IOException {
this.ownerFilesystem = ownerFilesystem; this.ownerFilesystem = ownerFilesystem;
if (baseBlock == -1) {
setBaseBlock(ownerFilesystem.getNextFreeBlock());
}
ownerFilesystem.allocateEntry(this);
} }
/** /**
@ -206,6 +210,7 @@ public abstract class DiskNode {
} }
public void addChild(DiskNode child) { public void addChild(DiskNode child) {
child.setParent(this);
children.add(child); children.add(child);
} }

View File

@ -125,7 +125,7 @@ public class FileNode extends DiskNode {
} }
public FileNode(ProdosVirtualDisk ownerFilesystem, File file) throws IOException { public FileNode(ProdosVirtualDisk ownerFilesystem, File file) throws IOException {
setOwnerFilesystem(ownerFilesystem); super(ownerFilesystem);
setPhysicalFile(file); setPhysicalFile(file);
setName(file.getName()); setName(file.getName());
} }
@ -141,10 +141,9 @@ public class FileNode extends DiskNode {
if (treeBlocks > 1) { if (treeBlocks > 1) {
treeBlocks++; treeBlocks++;
} }
for (int i = 0; i < dataBlocks + treeBlocks; i++) { for (int i = 1; i < dataBlocks + treeBlocks; i++) {
new SubNode(i, this); new SubNode(i, this);
} }
setBaseBlock(additionalNodes.get(0).getBaseBlock());
} }
@Override @Override
@ -153,6 +152,7 @@ public class FileNode extends DiskNode {
@Override @Override
public void readBlock(int block, byte[] buffer) throws IOException { public void readBlock(int block, byte[] buffer) throws IOException {
allocate();
int dataBlocks = (int) ((getPhysicalFile().length() + ProdosVirtualDisk.BLOCK_SIZE - 1) / ProdosVirtualDisk.BLOCK_SIZE); int dataBlocks = (int) ((getPhysicalFile().length() + ProdosVirtualDisk.BLOCK_SIZE - 1) / ProdosVirtualDisk.BLOCK_SIZE);
int treeBlocks = (((dataBlocks * 2) + (ProdosVirtualDisk.BLOCK_SIZE - 2)) / ProdosVirtualDisk.BLOCK_SIZE); int treeBlocks = (((dataBlocks * 2) + (ProdosVirtualDisk.BLOCK_SIZE - 2)) / ProdosVirtualDisk.BLOCK_SIZE);
if (treeBlocks > 1) { if (treeBlocks > 1) {
@ -167,7 +167,7 @@ public class FileNode extends DiskNode {
readFile(buffer, (block - 1)); readFile(buffer, (block - 1));
} else { } else {
// Generate seedling index block // Generate seedling index block
generateIndex(buffer, 1, treeBlocks + dataBlocks); generateIndex(buffer, 1, dataBlocks+1);
} }
break; break;
case TREE: case TREE:
@ -193,8 +193,8 @@ public class FileNode extends DiskNode {
private void generateIndex(byte[] buffer, int indexStart, int indexLimit) { private void generateIndex(byte[] buffer, int indexStart, int indexLimit) {
Arrays.fill(buffer, (byte) 0); Arrays.fill(buffer, (byte) 0);
for (int i = indexStart, count = 0; count < 256 && i < indexLimit && i < additionalNodes.size(); i++, count++) { for (int i = indexStart, count = 0; count < 256 && i < indexLimit && i <= additionalNodes.size(); i++, count++) {
int base = additionalNodes.get(i).baseBlock; int base = additionalNodes.get(i-1).getBaseBlock();
buffer[count] = (byte) (base & 0x0ff); buffer[count] = (byte) (base & 0x0ff);
buffer[count + 256] = (byte) (base >> 8); buffer[count + 256] = (byte) (base >> 8);
} }

View File

@ -27,8 +27,7 @@ import java.io.IOException;
public class FreespaceBitmap extends DiskNode { public class FreespaceBitmap extends DiskNode {
int size = (ProdosVirtualDisk.MAX_BLOCK + 1) / 8 / ProdosVirtualDisk.BLOCK_SIZE; int size = (ProdosVirtualDisk.MAX_BLOCK + 1) / 8 / ProdosVirtualDisk.BLOCK_SIZE;
public FreespaceBitmap(ProdosVirtualDisk fs, int start) throws IOException { public FreespaceBitmap(ProdosVirtualDisk fs, int start) throws IOException {
setBaseBlock(start); super(fs, start);
setOwnerFilesystem(fs);
for (int i=1; i < size; i++) { for (int i=1; i < size; i++) {
SubNode subNode = new SubNode(i, this, start+i); SubNode subNode = new SubNode(i, this, start+i);

View File

@ -52,6 +52,7 @@ public class ProdosVirtualDisk implements IDisk {
public ProdosVirtualDisk(File rootPath) throws IOException { public ProdosVirtualDisk(File rootPath) throws IOException {
ioBuffer = new byte[BLOCK_SIZE]; ioBuffer = new byte[BLOCK_SIZE];
initDiskStructure();
setPhysicalPath(rootPath); setPhysicalPath(rootPath);
} }
@ -131,25 +132,14 @@ public class ProdosVirtualDisk implements IDisk {
throw new IOException("Virtual Disk Full!"); throw new IOException("Virtual Disk Full!");
} }
// Mark space occupied by node
public void allocateEntry(DiskNode node) throws IOException {
physicalMap.put(node.baseBlock, node);
for (DiskNode subnode : node.additionalNodes) {
int blockNum = getNextFreeBlock();
subnode.setBaseBlock(blockNum);
physicalMap.put(blockNum, subnode);
}
}
// Mark space occupied by nodes as free (remove allocation mapping) // Mark space occupied by nodes as free (remove allocation mapping)
public void deallocateEntry(DiskNode node) { public void deallocateEntry(DiskNode node) {
// Only de-map nodes if the allocation table is actually pointing to the nodes! // Only de-map nodes if the allocation table is actually pointing to the nodes!
if (physicalMap.get(node.baseBlock) != null && physicalMap.get(node.baseBlock).equals(node)) { if (physicalMap.get(node.getBaseBlock()) != null && physicalMap.get(node.getBaseBlock()).equals(node)) {
physicalMap.remove(node.baseBlock); physicalMap.remove(node.getBaseBlock());
} }
node.additionalNodes.stream().filter((sub) node.additionalNodes.stream().filter((sub)
-> (physicalMap.get(sub.getBaseBlock()) != null && physicalMap.get(sub.baseBlock).equals(sub))). -> (physicalMap.get(sub.getBaseBlock()) != null && physicalMap.get(sub.getBaseBlock()).equals(sub))).
forEach((sub) -> { forEach((sub) -> {
physicalMap.remove(sub.getBaseBlock()); physicalMap.remove(sub.getBaseBlock());
}); });
@ -183,12 +173,16 @@ public class ProdosVirtualDisk implements IDisk {
return physicalRoot; return physicalRoot;
} }
public void setPhysicalPath(File f) throws IOException { private void initDiskStructure() throws IOException {
physicalMap = new HashMap<>();
freespaceBitmap = new FreespaceBitmap(this, FREESPACE_BITMAP_START);
}
private void setPhysicalPath(File f) throws IOException {
if (physicalRoot != null && physicalRoot.equals(f)) { if (physicalRoot != null && physicalRoot.equals(f)) {
return; return;
} }
physicalRoot = f; physicalRoot = f;
physicalMap = new HashMap<>();
if (!physicalRoot.exists() || !physicalRoot.isDirectory()) { if (!physicalRoot.exists() || !physicalRoot.isDirectory()) {
try { try {
throw new IOException("Root path must be a directory that exists!"); throw new IOException("Root path must be a directory that exists!");
@ -197,12 +191,9 @@ public class ProdosVirtualDisk implements IDisk {
} }
} }
// Root directory ALWAYS starts on block 2! // Root directory ALWAYS starts on block 2!
rootDirectory = new DirectoryNode(this, physicalRoot, VOLUME_START); rootDirectory = new DirectoryNode(this, physicalRoot, VOLUME_START, true);
rootDirectory.setName("VIRTUAL"); rootDirectory.setName("VIRTUAL");
allocateEntry(rootDirectory); rootDirectory.allocate();
freespaceBitmap = new FreespaceBitmap(this, FREESPACE_BITMAP_START);
allocateEntry(freespaceBitmap);
} }
@Override @Override

View File

@ -33,18 +33,18 @@ public class SubNode extends DiskNode {
int sequenceNumber; int sequenceNumber;
public SubNode(int seq, DiskNode parent) throws IOException { public SubNode(int seq, DiskNode parent) throws IOException {
super(parent.getOwnerFilesystem());
init(seq, parent); init(seq, parent);
} }
public SubNode(int seq, DiskNode parent, int baseBlock) throws IOException { public SubNode(int seq, DiskNode parent, int baseBlock) throws IOException {
setBaseBlock(baseBlock); super(parent.getOwnerFilesystem(), baseBlock);
init(seq, parent); init(seq, parent);
} }
private void init(int seq, DiskNode parent) throws IOException { private void init(int seq, DiskNode parent) throws IOException {
sequenceNumber = seq; sequenceNumber = seq;
setParent(parent); setParent(parent);
setOwnerFilesystem(parent.getOwnerFilesystem());
parent.additionalNodes.add(this); parent.additionalNodes.add(this);
} }