Rev 3408 |
Blame |
Compare with Previous |
Last modification |
View Log
| RSS feed
/*
* Copyright (C) 2006 Steve Ratcliffe
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program 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 General Public License for more details.
*
*
* Author: Steve Ratcliffe
* Create date: 30-Nov-2006
*/
package uk.me.parabola.imgfmt.sys;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import uk.me.parabola.imgfmt.Utils;
import uk.me.parabola.imgfmt.fs.DirectoryEntry;
import uk.me.parabola.imgfmt.fs.ImgChannel;
import uk.me.parabola.log.Logger;
/**
* An entry within a directory. This holds its name and a list
* of blocks that go to make up this file.
*
* A directory entry may take more than block in the file system.
*
* <p>All documentation seems to point to the block numbers having to be
* contiguous, but seems strange so I shall experiment.
*
* <p>Entries are in blocks of 512 bytes, regardless of the block size.
*
* @author Steve Ratcliffe
*/
class Dirent
implements DirectoryEntry
{
protected static final Logger log =
Logger.
getLogger(Dirent.
class);
// Constants.
static final int MAX_FILE_LEN =
8;
static final int MAX_EXT_LEN =
3;
// Offsets
static final int OFF_FILE_USED = 0x00
;
static final int OFF_NAME = 0x01
;
static final int OFF_EXT = 0x09
;
static final int OFF_FLAG = 0x10
;
static final int OFF_FILE_PART = 0x11
;
private static final int OFF_SIZE = 0x0c
;
// File names are a base+extension
private String name
;
private String ext
;
// The file size.
private int size
;
private final BlockManager blockManager
;
// The block table holds all the blocks that belong to this file. The
// documentation suggests that block numbers are always contiguous.
private final BlockTable blockTable
;
private boolean special
;
private static final int OFF_USED_FLAG =
0;
private boolean initialized
;
Dirent
(String name, BlockManager blockManager
) {
this.
blockManager = blockManager
;
int dot = name.
lastIndexOf('.');
if (dot
>=
0) {
setName
(name.
substring(0, dot
));
setExt
(name.
substring(dot+
1));
} else
throw new IllegalArgumentException("Filename did not have dot");
blockTable =
new BlockTable
();
}
/**
* Write this entry out to disk. Note that these are 512 bytes, regardless
* of the block size.
*
* @param file The file to write to.
* @throws IOException If writing fails for any reason.
*/
void sync
(ImgChannel file
) throws IOException {
int ntables = blockTable.
getNBlockTables();
ByteBuffer buf =
ByteBuffer.
allocate(ENTRY_SIZE
* ntables
);
buf.
order(ByteOrder.
LITTLE_ENDIAN);
for (int part =
0; part
< ntables
; part++
) {
log.
debug("position at part", part,
"is", buf.
position());
buf.
put((byte) 1);
buf.
put(Utils.
toBytes(name, MAX_FILE_LEN,
(byte) ' '));
buf.
put(Utils.
toBytes(ext, MAX_EXT_LEN,
(byte) ' '));
// Size is only present in the first part
if (part ==
0) {
log.
debug("dirent", name,
'.', ext,
"size is going to", size
);
buf.
putInt(size
);
} else {
buf.
putInt(0);
}
buf.
put((byte) (special
? 0x3:
0));
buf.
putChar((char) part
);
// Write out the allocation of blocks for this entry.
buf.
position(ENTRY_SIZE
* part + 0x20
);
blockTable.
writeTable(buf, part
);
}
buf.
flip();
file.
write(buf
);
}
/**
* Get the file name.
*
* @return The file name.
*/
public String getName
() {
return name
;
}
/**
* Get the file extension.
*
* @return The file extension.
*/
public String getExt
() {
return ext
;
}
/**
* The full name is of the form 8+3 with a dot in between the name and
* extension. The full name is used as the index in the directory.
*
* @return The full name.
*/
public String getFullName
() {
return name +
'.' + ext
;
}
/**
* Read in the block numbers from the given buffer. If this is the first
* directory block for this file, then the size is set too.
*
* @param buf The data as read from the file.
*/
void initBlocks
(ByteBuffer buf
) {
byte used = buf.
get(OFF_USED_FLAG
);
if (used
!=
1)
return;
int part = buf.
get(OFF_FILE_PART
) & 0xff
;
if (part ==
0 ||
(isSpecial
() && part ==
3))
size = buf.
getInt(OFF_SIZE
);
blockTable.
readTable(buf
);
initialized =
true;
}
/**
* Get the file size.
*
* @return The size of the file in bytes.
*/
public int getSize
() {
return size
;
}
/**
* Set the file name. The name should be exactly eight characters long
* and it is truncated or left padded with zeros to make this true.
*
* @param name The file name.
*/
private void setName
(String name
) {
int len = name.
length();
if (len
> MAX_FILE_LEN
) {
this.
name = name.
substring(0,
8);
} else if (len
< MAX_FILE_LEN
) {
StringBuffer sb =
new StringBuffer();
for (int i =
0; i
< MAX_FILE_LEN - len
; i++
) {
sb.
append('0');
}
sb.
append(name
);
this.
name = sb.
toString();
} else
this.
name = name
;
}
/**
* Set the file extension. Can't be longer than three characters.
* @param ext The file extension.
*/
private void setExt
(String ext
) {
log.
debug("ext len", ext.
length());
if (ext.
length() != MAX_EXT_LEN
)
throw new IllegalArgumentException("File extension is wrong size");
this.
ext = ext
;
}
/**
* The number of blocks that the header covers. The header includes
* the directory for the purposes of this routine.
*
* @return The total number of header basic blocks (blocks of 512 bytes).
*/
int numberHeaderBlocks
() {
return blockTable.
getNBlockTables();
}
void setSize
(int size
) {
if (log.
isDebugEnabled())
log.
debug("setting size", getName
(), getExt
(),
"to", size
);
this.
size = size
;
}
/**
* Add a block without increasing the size of the file.
*
* @param n The block number.
*/
void addBlock
(int n
) {
blockTable.
addBlock(n
);
}
/**
* Set for the first directory entry that covers the header and directory
* itself.
*
* @param special Set to true to mark as the special first entry.
*/
public void setSpecial
(boolean special
) {
this.
special = special
;
}
public boolean isSpecial
() {
return special
;
}
/**
* Converts from a logical block to a physical block. If the block does
* not exist then 0xffff will be returned.
*
* @param lblock The logical block in the file.
* @return The corresponding physical block in the filesystem.
*/
public int getPhysicalBlock
(int lblock
) {
return blockTable.
physFromLogical(lblock
);
}
public BlockManager getBlockManager
() {
return blockManager
;
}
protected void setInitialized
(boolean initialized
) {
this.
initialized = initialized
;
}
protected boolean isInitialized
() {
return initialized
;
}
}