From: <ls...@us...> - 2008-12-08 08:09:16
|
Revision: 4781 http://jnode.svn.sourceforge.net/jnode/?rev=4781&view=rev Author: lsantha Date: 2008-12-08 08:08:56 +0000 (Mon, 08 Dec 2008) Log Message: ----------- NTFS data in multiple attribute lists, by Daniel Noll Modified Paths: -------------- trunk/fs/src/fs/org/jnode/fs/ntfs/AttributeListAttribute.java trunk/fs/src/fs/org/jnode/fs/ntfs/AttributeListAttributeNonRes.java trunk/fs/src/fs/org/jnode/fs/ntfs/AttributeListAttributeRes.java trunk/fs/src/fs/org/jnode/fs/ntfs/AttributeListBlock.java trunk/fs/src/fs/org/jnode/fs/ntfs/AttributeListEntry.java trunk/fs/src/fs/org/jnode/fs/ntfs/FileRecord.java trunk/fs/src/fs/org/jnode/fs/ntfs/IndexAllocationAttribute.java trunk/fs/src/fs/org/jnode/fs/ntfs/IndexEntry.java trunk/fs/src/fs/org/jnode/fs/ntfs/MasterFileTable.java trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSEntry.java trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSIndex.java trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSNonResidentAttribute.java Modified: trunk/fs/src/fs/org/jnode/fs/ntfs/AttributeListAttribute.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/AttributeListAttribute.java 2008-12-08 07:14:48 UTC (rev 4780) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/AttributeListAttribute.java 2008-12-08 08:08:56 UTC (rev 4781) @@ -22,25 +22,22 @@ package org.jnode.fs.ntfs; import java.io.IOException; -import java.util.List; +import java.util.Iterator; /** * Common interface for both resident and non-resident attribute list * attributes. * - * @author Daniel Noll (da...@nu...) + * @author Daniel Noll (da...@no...) */ interface AttributeListAttribute { /** - * Gets an entry from the attribute list. + * Gets an iterator over all the entries in the attribute list. * - * XXX: What if there are multiple? In the case I've seen, there are multiple but only the first - * one contains any data. - * - * @param attrTypeID the type of attribute to find. - * @return the attribute entry. - * @throws IOException if there is an error reading the attribute's non-resident data. + * @return an iterator of all attribute list entries. + * @throws IOException if there is an error reading the attribute's data. */ - List<AttributeListEntry> getEntries(int attrTypeID) throws IOException; + Iterator<AttributeListEntry> getAllEntries() throws IOException; + } Modified: trunk/fs/src/fs/org/jnode/fs/ntfs/AttributeListAttributeNonRes.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/AttributeListAttributeNonRes.java 2008-12-08 07:14:48 UTC (rev 4780) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/AttributeListAttributeNonRes.java 2008-12-08 08:08:56 UTC (rev 4781) @@ -22,14 +22,14 @@ package org.jnode.fs.ntfs; import java.io.IOException; -import java.util.List; +import java.util.Iterator; /** * $ATTRIBUTE_LIST attribute, non-resident version. * * XXX: Is there a sensible way we can merge this with the resident version? * - * @author Daniel Noll (da...@nu...) + * @author Daniel Noll (da...@no...) */ final class AttributeListAttributeNonRes extends NTFSNonResidentAttribute implements AttributeListAttribute { @@ -43,16 +43,12 @@ } /** - * Gets an entry from the attribute list. + * Gets an iterator over all the entries in the attribute list. * - * XXX: What if there are multiple? In the case I've seen, there are multiple but only the first - * one contains any data. - * - * @param attrTypeID the type of attribute to find. - * @return the attribute entry. - * @throws IOException if there is an error reading the attribute's non-resident data. + * @return an iterator of all attribute list entries. + * @throws IOException if there is an error reading the attribute's data. */ - public List<AttributeListEntry> getEntries(int attrTypeID) throws IOException { + public Iterator<AttributeListEntry> getAllEntries() throws IOException { // Read the actual data from wherever it happens to be located. // TODO: Consider handling multiple data runs separately instead // of "glueing" them all together like this. @@ -60,7 +56,6 @@ final byte[] data = new byte[nrClusters * getFileRecord().getVolume().getClusterSize()]; readVCN(getStartVCN(), data, 0, nrClusters); AttributeListBlock listBlock = new AttributeListBlock(data, 0, getAttributeActualSize()); - return listBlock.getEntries(attrTypeID); + return listBlock.getAllEntries(); } - } Modified: trunk/fs/src/fs/org/jnode/fs/ntfs/AttributeListAttributeRes.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/AttributeListAttributeRes.java 2008-12-08 07:14:48 UTC (rev 4780) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/AttributeListAttributeRes.java 2008-12-08 08:08:56 UTC (rev 4781) @@ -22,14 +22,14 @@ package org.jnode.fs.ntfs; import java.io.IOException; -import java.util.List; +import java.util.Iterator; /** * $ATTRIBUTE_LIST attribute, resident version. * * XXX: Is there a sensible way we can merge this with the non-resident version? * - * @author Daniel Noll (da...@nu...) + * @author Daniel Noll (da...@no...) */ final class AttributeListAttributeRes extends NTFSResidentAttribute implements AttributeListAttribute { @@ -43,20 +43,16 @@ } /** - * Gets an entry from the attribute list. + * Gets an iterator over all the entries in the attribute list. * - * XXX: What if there are multiple? In the case I've seen, there are multiple but only the first - * one contains any data. - * - * @param attrTypeID the type of attribute to find. - * @return the attribute entry. - * @throws IOException if there is an error reading the attribute's non-resident data. + * @return an iterator of all attribute list entries. + * @throws IOException if there is an error reading the attribute's data. */ - public List<AttributeListEntry> getEntries(int attrTypeID) throws IOException { + public Iterator<AttributeListEntry> getAllEntries() throws IOException { final byte[] data = new byte[getAttributeLength()]; getData(getAttributeOffset(), data, 0, data.length); AttributeListBlock listBlock = new AttributeListBlock(data, 0, getAttributeLength()); - return listBlock.getEntries(attrTypeID); + return listBlock.getAllEntries(); } } Modified: trunk/fs/src/fs/org/jnode/fs/ntfs/AttributeListBlock.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/AttributeListBlock.java 2008-12-08 07:14:48 UTC (rev 4780) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/AttributeListBlock.java 2008-12-08 08:08:56 UTC (rev 4781) @@ -21,8 +21,8 @@ package org.jnode.fs.ntfs; -import java.util.ArrayList; -import java.util.List; +import java.util.Iterator; +import java.util.NoSuchElementException; /** * Data structure containing a list of {@link AttributeListEntry} entries. @@ -46,32 +46,71 @@ } /** - * Finds all entries from the attribute list with the given type ID. + * Gets an iterator over all the entries in the attribute list. * - * XXX: What if there are multiple? In the case I've seen, there are - * multiple but only the first one contains any data. - * - * @param attrTypeID the type of attribute to find. - * @return the attribute entry. + * @return an iterator of all attribute list entries. */ - public List<AttributeListEntry> getEntries(int attrTypeID) { - final List<AttributeListEntry> entries = new ArrayList<AttributeListEntry>(); - int offset = 0; - while (offset + 4 <= length) // Should be just (offset < length) but it seems we have some uneven lengths. - { - try { - int type = getUInt32AsInt(offset + 0x00); - if (type == attrTypeID) { - entries.add(new AttributeListEntry(this, offset)); - } + public Iterator<AttributeListEntry> getAllEntries() { + return new AttributeListEntryIterator(); + } - int length = getUInt16(offset + 0x04); - offset += length; - } catch (ArrayIndexOutOfBoundsException e) { - log.error("..."); + /** + * Iteration of attribute list entries. + */ + private class AttributeListEntryIterator implements Iterator<AttributeListEntry> { + + /** + * The next element to return. + */ + private AttributeListEntry nextElement; + + /** + * Current offset being looked at. + */ + private int offset = 0; + + /** + * Returns {@code true} if there are more elements in the iteration. + * + * @return {@code true} if there are more elements in the iteration. + */ + public boolean hasNext() { + // Safety check in case hasNext is called twice without calling next. + if (nextElement != null) { + return true; } + + // If the length is specified, use it to determine where the block ends. + if (offset + 4 > length) { + return false; + } + + int length = getUInt16(offset + 0x04); + nextElement = new AttributeListEntry(AttributeListBlock.this, offset); + offset += length; + return true; } - return entries; + + /** + * Gets the next entry from the iteration. + * + * @return the next entry from the iteration. + */ + public AttributeListEntry next() { + if (hasNext()) { + AttributeListEntry result = nextElement; + nextElement = null; + return result; + } else { + throw new NoSuchElementException("Iterator has no more entries"); + } + } + + /** + * @throws UnsupportedOperationException always. + */ + public void remove() { + throw new UnsupportedOperationException(); + } } - } Modified: trunk/fs/src/fs/org/jnode/fs/ntfs/AttributeListEntry.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/AttributeListEntry.java 2008-12-08 07:14:48 UTC (rev 4780) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/AttributeListEntry.java 2008-12-08 08:08:56 UTC (rev 4781) @@ -56,8 +56,33 @@ } /** + * Gets the length of the name. Not so useful for callers, hence private. + * @return the name length. + */ + private int getNameLength() { + return getUInt8(0x06); + } + + /** + * Gets the offset of the name. Not so useful for callers, hence private. + * @return the name offset (from the front of the entry.) + */ + private int getNameOffset() { + return getUInt8(0x07); + } + + /** + * Gets the starting VCN of the attribute, zero if the attribute is resident. + * @return the starting VCN. + */ + public int getStartingVCN() { + return getUInt16(0x08); + } + + /** * Gets the file reference number, which is the lowest 48 bits of the MFT - * reference. + * reference. This may point to the same file record which contains the + * attribute list. * * @return the file reference number. */ @@ -75,10 +100,46 @@ return getUInt48(0x16); } - // TODO: - // 0x06 1 Name length (N) - // 0x07 1 Offset to Name (a) - // 0x08 8 Starting VCN (b) - // 0x18 2 Attribute Id (c) - // 0x1A 2N Name in Unicode (if N >0) + /** + * Gets the ID of the attribute. This ID is unique within all attributes. + * @return the attribute ID. + */ + public int getAttributeID() { + return getUInt16(0x18); + } + + /** + * Gets the name of the attribute. Some attributes don't have names, and the names + * on attributes are supposedly unique within a given attribute type. + * + * @return the name of the attribute referenced by this entry. Returns the empty string + * if the attribute has no name. + */ + public String getName() { + final int nameLength = getNameLength(); + if (nameLength == 0) { + return ""; + } else { + char[] name = new char[nameLength]; + for (int i = 0, off = getNameOffset(); i < nameLength; i++, off += 2) { + name[i] = getChar16(off); + } + return new String(name); + } + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(super.toString()); + builder.append("[type=").append(getType()); + builder.append(",name=").append(getName()); + if (getStartingVCN() == 0) { + builder.append(",resident"); + } else { + builder.append(",ref=").append(getFileReferenceNumber()); + builder.append(",vcn=").append(getStartingVCN()); + } + builder.append(",id=").append(getAttributeID()).append("]"); + return builder.toString(); + } } Modified: trunk/fs/src/fs/org/jnode/fs/ntfs/FileRecord.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/FileRecord.java 2008-12-08 07:14:48 UTC (rev 4780) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/FileRecord.java 2008-12-08 08:08:56 UTC (rev 4781) @@ -22,6 +22,8 @@ package org.jnode.fs.ntfs; import java.io.IOException; +import java.util.Collections; +import java.util.Iterator; import java.util.List; import org.jnode.util.NumberUtils; @@ -34,28 +36,57 @@ */ class FileRecord extends NTFSRecord { + /** + * Sequence number of the file within the MFT. + */ + private long referenceNumber; + + /** + * Cached attribute list attribute. + */ + private AttributeListAttribute attributeListAttribute; + + /** + * Cached file name attribute. + */ private FileNameAttribute fileNameAttribute; /** * Initialize this instance. - * - * @param volume - * @param buffer + * + * @param volume reference to the NTFS volume. + * @param referenceNumber the reference number of the file within the MFT. + * @param buffer data buffer. + * @param offset offset into the buffer. */ - public FileRecord(NTFSVolume volume, byte[] buffer, int offset) throws IOException { + public FileRecord(NTFSVolume volume, long referenceNumber, byte[] buffer, int offset) throws IOException { super(volume, buffer, offset); + this.referenceNumber = referenceNumber; + + // Linux NTFS docs say there can only be one of these, so I'll believe them. + attributeListAttribute = (AttributeListAttribute) + findStoredAttributeByType(NTFSAttribute.Types.ATTRIBUTE_LIST); + // check for the magic number to see if we have a filerecord if (getMagic() != Magic.FILE) { throw new IOException("Invalid magic found: " + getMagic()); } + + // This additional sanity check is possible if the record also contains the MFT number. + // Helps catch bugs where a record is being read from the wrong offset. + final long storedReferenceNumber = getStoredReferenceNumber(); + if (storedReferenceNumber >= 0 && referenceNumber != storedReferenceNumber) { + throw new IOException("Stored reference number " + getStoredReferenceNumber() + + " does not match reference number " + referenceNumber); + } } /** * Gets the allocated size of the FILE record in bytes. * - * @return Returns the alocatedSize. + * @return Returns the allocated size. */ - public long getAlocatedSize() { + public long getAllocatedSize() { return getUInt32(0x1C); } @@ -69,12 +100,21 @@ } /** - * Is this a directory. + * Is this record in use? + * + * @return {@code true} if the record is in use. + */ + public boolean isInUse() { + return (getFlags() & 0x01) != 0; + } + + /** + * Is this a directory? * - * @return + * @return {@code true} if the record is a directory. */ public boolean isDirectory() { - return ((this.getFlags() & 0x02) != 0); + return (getFlags() & 0x02) != 0; } /** @@ -90,9 +130,9 @@ * Gets the byte offset to the first attribute in this mft record from the * start of the mft record. * - * @return Returns the firtAttributeOffset. + * @return the first attribute offset. */ - public int getFirtAttributeOffset() { + public int getFirstAttributeOffset() { return getUInt16(0x14); } @@ -131,6 +171,22 @@ } /** + * Gets the stored reference number. This can be compared against the reference number + * to confirm that the correct file record was returned, however it is not available + * on all versions of NTFS, and even on recent versions some MFT records lack it. + * + * @return the stored file reference number, or {@code -1} if it is not stored. + */ + public long getStoredReferenceNumber() { + // Expected to be 0x2A pre-XP. + if (getUpdateSequenceOffset() >= 0x30) { + return getUInt32(0x2C); + } else { + return -1; + } + } + + /** * Gets the name of this file. * * @return @@ -151,7 +207,7 @@ */ public FileNameAttribute getFileNameAttribute() { if (fileNameAttribute == null) { - fileNameAttribute = (FileNameAttribute) getAttribute(NTFSAttribute.Types.FILE_NAME); + fileNameAttribute = (FileNameAttribute) findAttributeByType(NTFSAttribute.Types.FILE_NAME); } return fileNameAttribute; } @@ -159,87 +215,133 @@ /** * Gets an attribute in this filerecord with a given id. * - * XXX: Returning an iterator of multiple might be better. + * @return an iteratover over attributes stored in this file record. + */ + public AttributeIterator getAllStoredAttributes() { + return new StoredAttributeIterator(); + } + + /** + * Finds a single stored attribute by ID. * - * @param attrTypeID the type ID of the attribute we're looking for. - * @return the attribute. + * @param id the ID. + * @return the attribute found, or {@code null} if not found. */ - public NTFSAttribute getAttribute(int attrTypeID) { - log.debug("getAttribute(0x" + NumberUtils.hex(attrTypeID, 4) + ")"); - int offset = this.getFirtAttributeOffset(); - while (true) { - final int type = getUInt32AsInt(offset + 0x00); - if (type == 0xFFFFFFFF) { - // The end of the attribute list - break; - } else if (type == attrTypeID) { - return NTFSAttribute.getAttribute(this, offset); - } else { - // Skip and go to the next - final int attrLength = getUInt32AsInt(offset + 0x04); - offset += attrLength; + private NTFSAttribute findStoredAttributeByID(int id) { + AttributeIterator iter = getAllStoredAttributes(); + NTFSAttribute attr; + while ((attr = iter.next()) != null) { + if (attr.getAttributeID() == id) { + return attr; } } + return null; + } - if (attrTypeID != NTFSAttribute.Types.ATTRIBUTE_LIST) { - final AttributeListAttribute attributeList = - (AttributeListAttribute) getAttribute(NTFSAttribute.Types.ATTRIBUTE_LIST); - if (attributeList != null) { - log.info("Has $ATTRIBUTE_LIST attribute"); + /** + * Finds a single stored attribute by type. + * + * @param typeID the type ID + * @return the attribute found, or {@code null} if not found. + * @see NTFSAttribute.Types + */ + private NTFSAttribute findStoredAttributeByType(int typeID) { + AttributeIterator iter = getAllStoredAttributes(); + NTFSAttribute attr; + while ((attr = iter.next()) != null) { + if (attr.getAttributeType() == typeID) { + return attr; + } + } + return null; + } - try { - final List<AttributeListEntry> entries = attributeList.getEntries(attrTypeID); - if (!entries.isEmpty()) { - log.debug("Found entries via $ATTRIBUTE_LIST: " + entries); - MasterFileTable mft = getVolume().getMFT(); - NTFSAttribute attribute = null; - for (AttributeListEntry entry : entries) { - // XXX: This is a little crappy as we should already know the exact offset - // of this attribute. This just makes the best of the API we already - // use everywhere else. - NTFSAttribute attr = - mft.getRecord(entry.getFileReferenceNumber()).getAttribute( - attrTypeID); + /** + * Gets an iterator over all attributes in this file record, including any attributes + * which are stored in other file records referenced from an $ATTRIBUTE_LIST attribute. + * + * @return an iterator over all attributes. + */ + private AttributeIterator getAllAttributes() { + if (attributeListAttribute == null) { + return getAllStoredAttributes(); + } else { + return new AttributeListAttributeIterator(); + } + } - if (attribute == null) { - // First attribute encountered. - attribute = attr; - if (!(attr instanceof NTFSNonResidentAttribute)) { - log - .info("Don't know how to glue together resident attributes, " - + "returning the first one alone"); - break; - } - } else { - // Subsequent attribute. - if (attr instanceof NTFSNonResidentAttribute) { - log.debug("Appending data runs onto parent attribute"); - ((NTFSNonResidentAttribute) attribute) - .appendDataRuns(((NTFSNonResidentAttribute) attr) - .getDataRuns()); - } else { - log.info("Don't know how to glue a resident attribute onto " - + "a non-resident one, skipping this attribute."); - } - } - } + /** + * Gets the first attribute in this filerecord with a given type. + * + * @param attrTypeID the type ID of the attribute we're looking for. + * @return the attribute. + */ + public NTFSAttribute findAttributeByType(int attrTypeID) { + log.debug("findAttributeByType(0x" + NumberUtils.hex(attrTypeID, 4) + ")"); - return attribute; - } - } catch (IOException e) { - log.error("IO error getting locating attribute list entry", e); - } + AttributeIterator iter = getAllAttributes(); + NTFSAttribute attr; + while ((attr = iter.next()) != null) { + if (attr.getAttributeType() == attrTypeID) { + log.debug("findAttributeByType(0x" + NumberUtils.hex(attrTypeID, 4) + ") found"); + return attr; } } - log.info("getAttribute(0x" + NumberUtils.hex(attrTypeID, 4) + ") not found"); + log.debug("findAttributeByType(0x" + NumberUtils.hex(attrTypeID, 4) + ") not found"); return null; } + /** + * Gets attributes in this filerecord with a given type and name. + * + * @param attrTypeID the type ID of the attribute we're looking for. + * @param name the name to look for. + * @return the attributes, will be empty if not found, never {@code null}. + */ + public AttributeIterator findAttributesByTypeAndName(final int attrTypeID, final String name) { + log.debug("findAttributesByTypeAndName(0x" + NumberUtils.hex(attrTypeID, 4) + + "," + name + ")"); + return new FilteredAttributeIterator(getAllAttributes()) { + @Override + protected boolean matches(NTFSAttribute attr) { + if (attr.getAttributeType() == attrTypeID) { + String attrName = attr.getAttributeName(); + if (name == null ? attrName == null : name.equals(attrName)) { + log.debug("findAttributesByTypeAndName(0x" + NumberUtils.hex(attrTypeID, 4) + + "," + name + ") found"); + return true; + } + } + return false; + } + }; + } + + /** + * Reads data from the file. + * + * @param fileOffset the offset into the file. + * @param dest the destination byte array into which to copy the file data. + * @param off the offset into the destination byte array. + * @param len the number of bytes of data to read. + * @throws IOException if an error occurs reading from the filesystem. + */ public void readData(long fileOffset, byte[] dest, int off, int len) throws IOException { - final NTFSAttribute data = this.getAttribute(NTFSAttribute.Types.DATA); - if (data.isResident()) { - final NTFSResidentAttribute resData = (NTFSResidentAttribute) data; + // Explicitly look for the attribute with no name, to avoid getting alternate streams. + // XXX: Add API for getting length and content from alternate streams. + final AttributeIterator dataAttrs = findAttributesByTypeAndName(NTFSAttribute.Types.DATA, null); + NTFSAttribute attr = dataAttrs.next(); + if (attr == null) { + throw new IOException("Data attribute not found, file record = " + this); + } + + if (attr.isResident()) { + if (dataAttrs.next() != null) { + throw new IOException("Resident attribute should be by itself, file record = " + this); + } + + final NTFSResidentAttribute resData = (NTFSResidentAttribute) attr; final int attrLength = resData.getAttributeLength(); if (attrLength < len) { throw new IOException("File data(" + attrLength + @@ -252,12 +354,167 @@ // calculate start and end cluster final int clusterSize = getVolume().getClusterSize(); - final long startCluster = (fileOffset / clusterSize); - final int nrClusters = (int) ((len + (fileOffset % clusterSize)) / clusterSize) + 1; - final NTFSNonResidentAttribute nresData = (NTFSNonResidentAttribute) data; + final long startCluster = fileOffset / clusterSize; + final long endCluster = (fileOffset + len - 1) / clusterSize; + final int nrClusters = (int) (endCluster - startCluster + 1); + final byte[] tmp = new byte[nrClusters * clusterSize]; - final byte[] tmp = new byte[nrClusters * clusterSize]; - nresData.readVCN(startCluster, tmp, 0, nrClusters); + long clusterWithinNresData = startCluster; + int readClusters = 0; + do { + if (attr.isResident()) { + throw new IOException("Resident attribute should be by itself, file record = " + this); + } + + final NTFSNonResidentAttribute nresData = (NTFSNonResidentAttribute) attr; + + readClusters += nresData.readVCN(clusterWithinNresData, tmp, 0, nrClusters); + if (readClusters == nrClusters) { + // Already done. + break; + } + + // When there are multiple attributes, the data in each one claims to start at VCN 0. + // Clearly this is not the case, so we need to offset when we read. + clusterWithinNresData -= nresData.getNumberOfVCNs(); + attr = dataAttrs.next(); + } while (attr != null); + + if (readClusters != nrClusters) { + throw new IOException("Requested " + nrClusters + " clusters but only read " + readClusters); + } + System.arraycopy(tmp, (int) fileOffset % clusterSize, dest, off, len); } + + public String toString() { + if (isInUse()) { + return super.toString() + "[fileName=" + getFileName() + "]"; + } else { + return super.toString() + "[unused]"; + } + } + + /** + * Iterator over multiple attributes, where those attributes are stored in an + * attribute list instead of directly in the file record. + */ + private class AttributeListAttributeIterator extends AttributeIterator { + + /** + * Current iterator over attribute list entries. + */ + private Iterator<AttributeListEntry> entryIterator; + + /** + * Constructs the iterator. + */ + private AttributeListAttributeIterator() { + try { + entryIterator = attributeListAttribute.getAllEntries(); + } catch (IOException e) { + log.error("Error getting attributes from attribute list, file record " + FileRecord.this, e); + List<AttributeListEntry> emptyList = Collections.emptyList(); + entryIterator = emptyList.iterator(); + } + } + + @Override + protected NTFSAttribute next() { + while (entryIterator.hasNext()) { + AttributeListEntry entry = entryIterator.next(); + try { + // If it's resident (i.e. in the current file record) then we don't need to + // look it up, and doing so would risk infinite recursion. + FileRecord holdingRecord; + if (entry.getFileReferenceNumber() == referenceNumber) { + holdingRecord = FileRecord.this; + } else { + holdingRecord = getVolume().getMFT().getRecord(entry.getFileReferenceNumber()); + } + + return holdingRecord.findStoredAttributeByID(entry.getAttributeID()); + } catch (IOException e) { + // XXX: I wish the iterator could just throw this error out. Should we + // just create a different kind of iterator class instead? + log.error("Error getting MFT or FileRecord for attribute in list, ref = 0x" + + Long.toHexString(entry.getFileReferenceNumber()), e); + } + } + + return null; + } + } + + /** + * Iterator over stored attributes in this file record. + */ + private class StoredAttributeIterator extends AttributeIterator { + /** + * The next attribute offset to look at. + */ + private int nextOffset = getFirstAttributeOffset(); + + @Override + protected NTFSAttribute next() { + final int offset = nextOffset; + final int type = getUInt32AsInt(offset + 0x00); + if (type == 0xFFFFFFFF) { + // Normal end of list condition. + return null; + } else { + NTFSAttribute attribute = NTFSAttribute.getAttribute(FileRecord.this, offset); + int offsetToNextOffset = getUInt32AsInt(offset + 0x04); + if (offsetToNextOffset <= 0) { + log.error("Non-positive offset, preventing infinite loop. Data on disk may be corrupt. " + + "referenceNumber = " + referenceNumber); + return null; + } + + nextOffset += offsetToNextOffset; + return attribute; + } + } + } + + /** + * An iterator for filtering another iterator. + */ + private abstract class FilteredAttributeIterator extends AttributeIterator { + private AttributeIterator inner; + private FilteredAttributeIterator(AttributeIterator inner) { + this.inner = inner; + } + + @Override + protected NTFSAttribute next() { + NTFSAttribute attr; + while ((attr = inner.next()) != null) { + if (matches(attr)) { + return attr; + } + } + return null; + } + + /** + * Implemented by subclasses to perform matching logic. + * + * @param attr the attribute. + * @return {@code true} if it matches, {@code false} otherwise. + */ + protected abstract boolean matches(NTFSAttribute attr); + } + + /** + * Holds code common to both types of attribute list. + */ + private abstract class AttributeIterator { + /** + * Gets the next element from the iterator. + * + * @return the next element from the iterator. Returns {@code null} at the end. + */ + protected abstract NTFSAttribute next(); + } } Modified: trunk/fs/src/fs/org/jnode/fs/ntfs/IndexAllocationAttribute.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/IndexAllocationAttribute.java 2008-12-08 07:14:48 UTC (rev 4780) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/IndexAllocationAttribute.java 2008-12-08 08:08:56 UTC (rev 4781) @@ -47,9 +47,26 @@ public IndexBlock getIndexBlock(IndexRoot indexRoot, long vcn) throws IOException { log.debug("getIndexBlock(..," + vcn + ")"); final FileRecord fileRecord = getFileRecord(); - final int nrClusters = indexRoot.getClustersPerIndexBlock(); - final byte[] data = new byte[nrClusters * fileRecord.getVolume().getClusterSize()]; - readVCN(vcn, data, 0, nrClusters); - return new IndexBlock(fileRecord, data, 0); + + // VCN passed in is relative to the size of index clusters, not filesystem clusters. + // Calculate the actual offset we need in terms of filesystem clusters, + // and how many actual clusters we will need to read. + + final int indexBlockSize = indexRoot.getIndexBlockSize(); + final int indexClusterSize = indexBlockSize / indexRoot.getClustersPerIndexBlock(); + final int fsClusterSize = fileRecord.getVolume().getClusterSize(); + final long fsVcn = vcn * indexClusterSize / fsClusterSize; + final int fsNrClusters = (indexBlockSize - 1) / fsClusterSize + 1; + final int offsetIntoVcn = (int) ((vcn * indexClusterSize) % fsClusterSize); + + final byte[] data = new byte[fsNrClusters * fsClusterSize]; + final int readClusters = readVCN(fsVcn, data, 0, fsNrClusters); + if (readClusters != fsNrClusters) { + // If we don't throw an error now, it just fails more mysteriously later! + throw new IOException("Number of clusters read was not the number requested (requested " + + fsNrClusters + ", read " + readClusters + ")"); + } + + return new IndexBlock(fileRecord, data, offsetIntoVcn); } } Modified: trunk/fs/src/fs/org/jnode/fs/ntfs/IndexEntry.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/IndexEntry.java 2008-12-08 07:14:48 UTC (rev 4780) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/IndexEntry.java 2008-12-08 08:08:56 UTC (rev 4781) @@ -55,7 +55,7 @@ } public boolean hasSubNodes() { - return (getFlags() & 0x01) != 0; + return (getIndexFlags() & 0x01) != 0; } /** @@ -70,12 +70,12 @@ * Gets the flags of this index entry. * @return */ - public int getFlags() { + public int getIndexFlags() { return getUInt8(0x0C); } public boolean isLastIndexEntryInSubnode() { - return (getFlags() & 0x02) != 0; + return (getIndexFlags() & 0x02) != 0; } /** @@ -88,7 +88,7 @@ } public boolean isDirectory() { - return (getUInt32(0x48) & 0x10000000L) != 0; + return (getFileFlags() & 0x10000000L) != 0; } public String getFileName() { @@ -126,4 +126,12 @@ } return name; } + + @Override + public String toString() { + return super.toString() + + "[fileName=" + getFileName() + + ",indexFlags=" + getIndexFlags() + + ",fileFlags=" + getFileFlags() + "]"; + } } Modified: trunk/fs/src/fs/org/jnode/fs/ntfs/MasterFileTable.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/MasterFileTable.java 2008-12-08 07:14:48 UTC (rev 4780) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/MasterFileTable.java 2008-12-08 08:08:56 UTC (rev 4781) @@ -123,7 +123,7 @@ * @throws IOException */ public MasterFileTable(NTFSVolume volume, byte[] buffer, int offset) throws IOException { - super(volume, buffer, offset); + super(volume, SystemFiles.MFT, buffer, offset); } /** @@ -142,7 +142,7 @@ // read the buffer final byte[] buffer = new byte[bytesPerFileRecord]; readData(offset, buffer, 0, bytesPerFileRecord); - return new FileRecord(volume, buffer, 0); + return new FileRecord(volume, index, buffer, 0); } public FileRecord getIndexedFileRecord(IndexEntry indexEntry) throws IOException { Modified: trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSEntry.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSEntry.java 2008-12-08 07:14:48 UTC (rev 4780) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSEntry.java 2008-12-08 08:08:56 UTC (rev 4781) @@ -138,6 +138,7 @@ public FSDirectory getDirectory() throws IOException { if (this.isDirectory()) { if (cachedFSObject == null) { + // XXX: Why can't this just use getFileRecord()? cachedFSObject = new NTFSDirectory(fs, getFileRecord().getVolume().getMFT() .getIndexedFileRecord(indexEntry)); @@ -194,4 +195,9 @@ public boolean isDirty() throws IOException { return true; } + + @Override + public String toString() { + return super.toString() + '(' + indexEntry + ')'; + } } Modified: trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSIndex.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSIndex.java 2008-12-08 07:14:48 UTC (rev 4780) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSIndex.java 2008-12-08 08:08:56 UTC (rev 4781) @@ -62,7 +62,7 @@ public IndexRootAttribute getIndexRootAttribute() { if (indexRootAttribute == null) { indexRootAttribute = (IndexRootAttribute) - fileRecord.getAttribute(NTFSAttribute.Types.INDEX_ROOT); + fileRecord.findAttributeByType(NTFSAttribute.Types.INDEX_ROOT); log.debug("getIndexRootAttribute: " + indexRootAttribute); } return indexRootAttribute; @@ -76,7 +76,7 @@ public IndexAllocationAttribute getIndexAllocationAttribute() { if (indexAllocationAttribute == null) { indexAllocationAttribute = (IndexAllocationAttribute) - fileRecord.getAttribute(NTFSAttribute.Types.INDEX_ALLOCATION); + fileRecord.findAttributeByType(NTFSAttribute.Types.INDEX_ALLOCATION); } return indexAllocationAttribute; } Modified: trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSNonResidentAttribute.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSNonResidentAttribute.java 2008-12-08 07:14:48 UTC (rev 4780) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSNonResidentAttribute.java 2008-12-08 08:08:56 UTC (rev 4781) @@ -91,7 +91,7 @@ * * @return the size allocated to the attribute. */ - public long getAttributeAlocatedSize() { + public long getAttributeAllocatedSize() { return getUInt32(0x28); } @@ -125,11 +125,13 @@ } // check the dataruns final int clusterSize = getFileRecord().getVolume().getClusterSize(); - if (this.numberOfVCNs != this.getAttributeAlocatedSize() / clusterSize) { - log.error("ERROR: The number of VCNs from the data runs is different than the allocated size!: - " + - this.numberOfVCNs); - log.error("Alocatedsize = " + getAttributeAlocatedSize() / clusterSize); - log.error("number of data runs = " + dataRuns.size()); + // Rounds up but won't work for 0, which shouldn't occur here. + final long allocatedVCNs = (getAttributeAllocatedSize() - 1) / clusterSize + 1; + if (this.numberOfVCNs != allocatedVCNs) { + // Probably not a problem, often multiple attributes make up one allocation. + log.debug("VCN mismatch between data runs and allocated size, possibly a composite attribute. " + + "data run VCNs = " + this.numberOfVCNs + ", allocated size = " + allocatedVCNs + + ", data run count = " + dataRuns.size()); } } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ls...@us...> - 2008-12-15 08:40:47
|
Revision: 4789 http://jnode.svn.sourceforge.net/jnode/?rev=4789&view=rev Author: lsantha Date: 2008-12-15 08:40:42 +0000 (Mon, 15 Dec 2008) Log Message: ----------- Support for compressed files in NTFS, by Daniel Noll. Modified Paths: -------------- trunk/fs/src/fs/org/jnode/fs/ntfs/AttributeListBlock.java trunk/fs/src/fs/org/jnode/fs/ntfs/AttributeListEntry.java trunk/fs/src/fs/org/jnode/fs/ntfs/DataRun.java trunk/fs/src/fs/org/jnode/fs/ntfs/FileRecord.java trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSAttribute.java trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSNonResidentAttribute.java Added Paths: ----------- trunk/fs/src/fs/org/jnode/fs/ntfs/CompressedDataRun.java trunk/fs/src/fs/org/jnode/fs/ntfs/DataRunInterface.java Modified: trunk/fs/src/fs/org/jnode/fs/ntfs/AttributeListBlock.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/AttributeListBlock.java 2008-12-14 21:49:21 UTC (rev 4788) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/AttributeListBlock.java 2008-12-15 08:40:42 UTC (rev 4789) @@ -27,7 +27,7 @@ /** * Data structure containing a list of {@link AttributeListEntry} entries. * - * @author Daniel Noll (da...@nu...) + * @author Daniel Noll (da...@no...) */ final class AttributeListBlock extends NTFSStructure { @@ -39,6 +39,7 @@ /** * @param data binary data for the block. * @param offset the offset into the binary data. + * @param length the length of the attribute list block, or 0 if unknown. */ public AttributeListBlock(byte[] data, int offset, long length) { super(data, offset); Modified: trunk/fs/src/fs/org/jnode/fs/ntfs/AttributeListEntry.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/AttributeListEntry.java 2008-12-14 21:49:21 UTC (rev 4788) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/AttributeListEntry.java 2008-12-15 08:40:42 UTC (rev 4789) @@ -23,7 +23,7 @@ /** - * @author Daniel Noll (da...@nu...) + * @author Daniel Noll (da...@no...) */ final class AttributeListEntry extends NTFSStructure { @@ -97,7 +97,7 @@ * @return the file sequence number. */ public long getFileSequenceNumber() { - return getUInt48(0x16); + return getUInt16(0x16); } /** Added: trunk/fs/src/fs/org/jnode/fs/ntfs/CompressedDataRun.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/CompressedDataRun.java (rev 0) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/CompressedDataRun.java 2008-12-15 08:40:42 UTC (rev 4789) @@ -0,0 +1,336 @@ +/* + * $Id$ + * + * JNode.org + * Copyright (C) 2003-2006 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.fs.ntfs; + +import java.io.IOException; +import java.util.Arrays; + +import org.apache.log4j.Logger; +import org.jnode.util.LittleEndian; + +/** + * @author Daniel Noll (da...@no...) + */ +final class CompressedDataRun implements DataRunInterface { + /** + * Size of a compressed block in NTFS. This is always the same even if the cluster size + * is not 4k. + */ + private static final int BLOCK_SIZE = 0x1000; + + /** + * Logger. + */ + private static final Logger log = Logger.getLogger(CompressedDataRun.class); + + /** + * The underlying data run containing the compressed data. + */ + private final DataRun compressedRun; + + /** + * The number of clusters which make up a compression unit. + */ + private final int compressionUnitSize; + + /** + * Constructs a compressed run which when read, will decrypt data found + * in the provided data run. + * + * @param compressedRun the compressed data run. + * @param compressionUnitSize the number of clusters which make up a compression unit. + */ + public CompressedDataRun(DataRun compressedRun, int compressionUnitSize) { + this.compressedRun = compressedRun; + this.compressionUnitSize = compressionUnitSize; + } + + /** + * Gets the length of the data run in clusters. + * + * @return the length of the run in clusters. + */ + public int getLength() { + return compressionUnitSize; + } + + /** + * Reads clusters from this datarun. + * + * @param vcn the VCN to read, offset from the start of the entire file. + * @param dst destination buffer. + * @param dstOffset offset into destination buffer. + * @param nrClusters number of clusters to read. + * @param clusterSize size of each cluster. + * @param volume reference to the NTFS volume structure. + * @return the number of clusters read. + * @throws IOException if an error occurs reading. + */ + public int readClusters(long vcn, byte[] dst, int dstOffset, + int nrClusters, int clusterSize, NTFSVolume volume) throws IOException { + + // Logic to determine whether we own the VCN which has been requested. + // XXX: Lifted from DataRun. Consider moving to some good common location. + final long myFirstVcn = compressedRun.getFirstVcn(); + final int myLength = getLength(); + final long myLastVcn = myFirstVcn + myLength - 1; + final long reqLastVcn = vcn + nrClusters - 1; + log.debug("me:" + myFirstVcn + "-" + myLastVcn + ", req:" + vcn + "-" + reqLastVcn); + if ((vcn > myLastVcn) || (myFirstVcn > reqLastVcn)) { + // Not my region + return 0; + } + + // Now we know it's in our data run, here's the actual fragment to read. + final long actFirstVcn = Math.max(myFirstVcn, vcn); + final int actLength = (int) (Math.min(myLastVcn, reqLastVcn) - actFirstVcn + 1); + + // This is the actual number of stored clusters after compression. + // If the number of stored clusters is the same as the compression unit size, + // then the data can be read directly without decompressing it. + final int compClusters = compressedRun.getLength(); + if (compClusters == compressionUnitSize) { + return compressedRun.readClusters(vcn, dst, dstOffset, compClusters, + clusterSize, volume); + } + + // Now we know the data is compressed. Read in the compressed block... + final int vcnOffsetWithinUnit = (int) (actFirstVcn % compressionUnitSize); + final long compFirstVcn = actFirstVcn - vcnOffsetWithinUnit; + final byte[] tempCompressed = new byte[compClusters * clusterSize]; + final int read = compressedRun.readClusters(compFirstVcn, tempCompressed, 0, + compClusters, clusterSize, volume); + if (read != compClusters) { + throw new IOException("Needed " + compClusters + " clusters but could " + + "only read " + read); + } + + // Uncompress it, and copy into the destination. + final byte[] tempUncompressed = new byte[compressionUnitSize * clusterSize]; + // XXX: We could potentially reduce the overhead by modifying the compression + // routine such that it's capable of skipping chunks that aren't needed. + uncompressUnit(tempCompressed, tempUncompressed); + + System.arraycopy(tempUncompressed, vcnOffsetWithinUnit * clusterSize, + dst, dstOffset + (int) (actFirstVcn - vcn) * clusterSize, + actLength * clusterSize); + + return actLength; + } + + /** + * Uncompresses a single unit of multiple compressed blocks. + * + * @param compressed the compressed data (in.) + * @param uncompressed the uncompressed data (out.) + * @param requiredBytes the number of bytes needed before the read can stop. + * @throws IOException if the decompression fails. + */ + private static void uncompressUnit(final byte[] compressed, + final byte[] uncompressed) throws IOException { + + // This is just a convenient way to simulate the original code's pointer arithmetic. + // I tried using buffers but positions in those are always from the beginning and + // I had to also maintain a position from the start of the current block. + final OffsetByteArray compressedData = new OffsetByteArray(compressed); + final OffsetByteArray uncompressedData = new OffsetByteArray(uncompressed); + + for (int i = 0; i * BLOCK_SIZE < uncompressed.length; i++) { + final int consumed = uncompressBlock(compressedData, uncompressedData); + + // Apple's code had this as an error but to me it looks like this simply + // terminates the sequence of compressed blocks. + if (consumed == 0) { + // At the current point in time this is already zero but if the code + // changes in the future to reuse the temp buffer, this is a good idea. + uncompressedData.zero(0, uncompressed.length - uncompressedData.offset); + break; + } + + compressedData.offset += consumed; + uncompressedData.offset += BLOCK_SIZE; + } + } + + /** + * Uncompresses a single block. + * + * @param compressed the compressed buffer (in.) + * @param uncompressed the uncompressed buffer (out.) + * @return the number of bytes consumed from the compressed buffer. + */ + private static int uncompressBlock(final OffsetByteArray compressed, + final OffsetByteArray uncompressed) { + + int pos = 0, cpos = 0; + + final int rawLen = compressed.getShort(cpos); cpos += 2; + final int len = rawLen & 0xFFF; + log.debug("ntfs_uncompblock: block length: " + len + " + 3, 0x" + + Integer.toHexString(len) + ",0x" + Integer.toHexString(rawLen)); + + if (rawLen == 0) { + // End of sequence, rest is zero. For some reason there is nothing + // of the sort documented in the Linux kernel's description of compression. + return 0; + } + + if ((rawLen & 0x8000) == 0) { + // Uncompressed chunks store length as 0xFFF always. + if ((len + 1) != BLOCK_SIZE) { + log.debug("ntfs_uncompblock: len: " + len + " instead of 0xfff"); + } + + // Copies the entire compression block as-is, need to skip the compression flag, + // no idea why they even stored it given that it isn't used. + // Darwin's version I was referring to doesn't skip this, which seems be a bug. + cpos++; + uncompressed.copyFrom(compressed, cpos, 0, len + 1); + uncompressed.zero(len + 1, BLOCK_SIZE - 1 - len); + return len + 3; + } + + while (cpos < len + 3 && pos < BLOCK_SIZE) { + byte ctag = compressed.get(cpos++); + for (int i = 0; i < 8 && pos < BLOCK_SIZE; i++) { + if ((ctag & 1) != 0) { + int j, lmask, dshift; + for (j = pos - 1, lmask = 0xFFF, dshift = 12; + j >= 0x10; j >>= 1) { + dshift--; + lmask >>= 1; + } + final int tmp = compressed.getShort(cpos); cpos += 2; + final int boff = -1 - (tmp >> dshift); + final int blen = Math.min(3 + (tmp & lmask), BLOCK_SIZE - pos); + + // Note that boff is negative. + uncompressed.copyFrom(uncompressed, pos + boff, pos, blen); + pos += blen; + } else { + uncompressed.put(pos++, compressed.get(cpos++)); + } + ctag >>= 1; + } + } + + return len + 3; + } + + /** + * Convenience class wrapping an array with its offset. An alternative to pointer + * arithmetic without going to the level of using an NIO buffer. + */ + private static class OffsetByteArray { + + /** + * The contained array. + */ + private final byte[] array; + + /** + * The current offset. + */ + private int offset; + + /** + * Constructs the offset byte array. The offset begins at zero. + * + * @param array the contained array. + */ + private OffsetByteArray(final byte[] array) { + this.array = array; + } + + /** + * Gets a single byte from the array. + * + * @param offset the offset from the contained offset. + * @return the byte. + */ + private byte get(int offset) { + return array[this.offset + offset]; + } + + /** + * Puts a single byte into the array. + * + * @param offset the offset from the contained offset. + * @param value the byte. + */ + private void put(int offset, byte value) { + array[this.offset + offset] = value; + } + + /** + * Gets a 16-bit little-endian value from the array. + * + * @param offset the offset from the contained offset. + * @return the short. + */ + private int getShort(int offset) { + return LittleEndian.getUInt16(array, this.offset + offset); + } + + /** + * Copies a slice from the provided array into our own array. Uses {@code System.arraycopy} + * where possible; if the slices overlap, copies one byte at a time to avoid a problem with + * using {@code System.arraycopy} in this situation. + * + * @param src the source offset byte array. + * @param srcOffset offset from the source array's offset. + * @param destOffset offset from our own offset. + * @param length the number of bytes to copy. + */ + private void copyFrom(OffsetByteArray src, int srcOffset, int destOffset, int length) { + int realSrcOffset = src.offset + srcOffset; + int realDestOffset = offset + destOffset; + byte[] srcArray = src.array; + byte[] destArray = array; + + // If the arrays are the same and the slices overlap we can't use the optimisation + // because System.arraycopy effectively copies to a temp area. :-( + if (srcArray == destArray && + (realSrcOffset < realDestOffset && realSrcOffset + length > realDestOffset || + realDestOffset < realSrcOffset && realDestOffset + length > realSrcOffset)) { + + for (int i = 0; i < length; i++) { + destArray[realDestOffset + i] = srcArray[realSrcOffset + i]; + } + + return; + } + + System.arraycopy(srcArray, realSrcOffset, destArray, realDestOffset, length); + } + + /** + * Zeroes out elements of the array. + * + * @param offset the offset from the contained offset. + * @param length the number of sequential bytes to zero out. + */ + private void zero(int offset, int length) { + Arrays.fill(array, this.offset + offset, this.offset + offset + length, (byte) 0); + } + } +} Modified: trunk/fs/src/fs/org/jnode/fs/ntfs/DataRun.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/DataRun.java 2008-12-14 21:49:21 UTC (rev 4788) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/DataRun.java 2008-12-15 08:40:42 UTC (rev 4789) @@ -27,7 +27,7 @@ /** * @author Ewout Prangsma (ep...@us...) */ -final class DataRun extends NTFSStructure { +final class DataRun extends NTFSStructure implements DataRunInterface { /** Type of this datarun */ private final int type; @@ -41,6 +41,9 @@ /** Length of datarun in clusters */ private final int length; + /** Flag indicating that the data is not stored on disk but is all zero. */ + private boolean sparse = false; + /** Size in bytes of this datarun descriptor */ private final int size; @@ -87,6 +90,7 @@ final int cluster; switch (clusterlen) { case 0x00: + sparse = true; cluster = 0; break; case 0x01: @@ -108,6 +112,17 @@ } /** + * Tests if this data run is a sparse run. Sparse runs don't actually refer to + * stored data, and are effectively a way to store a run of zeroes without storage + * penalty. + * + * @return {@code true} if the run is sparse, {@code false} if it is not. + */ + public boolean isSparse() { + return sparse; + } + + /** * @return Returns the cluster. */ public long getCluster() { @@ -137,20 +152,11 @@ * * @return Returns the vcn. */ - public final long getFirstVcn() { + public long getFirstVcn() { return this.vcn; } /** - * Sets the first VCN of this datarun. - * - * @param vcn the new VCN. - */ - final void setFirstVcn(long vcn) { - this.vcn = vcn; - } - - /** * Read clusters from this datarun. * * @param vcn @@ -171,7 +177,9 @@ final long reqLastVcn = vcn + nrClusters - 1; - log.debug("me:" + myFirstVcn + "-" + myLastVcn + ", req:" + vcn + "-" + reqLastVcn); + if (log.isDebugEnabled()) { + log.debug("me:" + myFirstVcn + "-" + myLastVcn + ", req:" + vcn + "-" + reqLastVcn); + } if ((vcn > myLastVcn) || (myFirstVcn > reqLastVcn)) { // Not my region @@ -194,10 +202,12 @@ actCluster = getCluster() + vcnDelta; } - log.debug("cluster=" + cluster + ", length=" + length + ", dstOffset=" + dstOffset); - log.debug("cnt=" + count + ", actclu=" + actCluster + ", actdstoff=" + actDstOffset); + if (log.isDebugEnabled()) { + log.debug("cluster=" + cluster + ", length=" + length + ", dstOffset=" + dstOffset); + log.debug("cnt=" + count + ", actclu=" + actCluster + ", actdstoff=" + actDstOffset); + } - if (actCluster == 0) { + if (isSparse()) { // Not really stored on disk -- sparse files, etc. Arrays.fill(dst, actDstOffset, actDstOffset + count * clusterSize, (byte) 0); } else { Added: trunk/fs/src/fs/org/jnode/fs/ntfs/DataRunInterface.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/DataRunInterface.java (rev 0) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/DataRunInterface.java 2008-12-15 08:40:42 UTC (rev 4789) @@ -0,0 +1,52 @@ +/* + * $Id$ + * + * JNode.org + * Copyright (C) 2003-2006 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.fs.ntfs; + +import java.io.IOException; + +/** + * @author Daniel Noll (da...@no...) + */ +interface DataRunInterface { + + /** + * Gets the length of the data run in clusters. + * + * @return the length of the run in clusters. + */ + int getLength(); + + /** + * Reads clusters from this datarun. + * + * @param vcn the VCN to read, offset from the start of the entire file. + * @param dst destination buffer. + * @param dstOffset offset into destination buffer. + * @param nrClusters number of clusters to read. + * @param clusterSize size of each cluster. + * @param volume reference to the NTFS volume structure. + * @return the number of clusters read. + * @throws IOException if an error occurs reading. + */ + public int readClusters(long vcn, byte[] dst, int dstOffset, + int nrClusters, int clusterSize, NTFSVolume volume) throws IOException; +} Modified: trunk/fs/src/fs/org/jnode/fs/ntfs/FileRecord.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/FileRecord.java 2008-12-14 21:49:21 UTC (rev 4788) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/FileRecord.java 2008-12-15 08:40:42 UTC (rev 4789) @@ -33,6 +33,7 @@ * * @author Chira * @author Ewout Prangsma (ep...@us...) + * @author Daniel Noll (da...@no...) (new attribute iteration support) */ class FileRecord extends NTFSRecord { @@ -67,8 +68,22 @@ attributeListAttribute = (AttributeListAttribute) findStoredAttributeByType(NTFSAttribute.Types.ATTRIBUTE_LIST); - // check for the magic number to see if we have a filerecord + // check for the magic numberb to see if we have a filerecord if (getMagic() != Magic.FILE) { + log.debug("Invalid magic number found for FILE record: " + getMagic() + " -- dumping buffer"); + for (int off = 0; off < buffer.length; off += 32) { + StringBuilder builder = new StringBuilder(); + for (int i = off; i < off + 32 && i < buffer.length; i++) { + String hex = Integer.toHexString(buffer[i]); + while (hex.length() < 2) { + hex = '0' + hex; + } + + builder.append(' ').append(hex); + } + log.debug(builder.toString()); + } + throw new IOException("Invalid magic found: " + getMagic()); } @@ -107,7 +122,7 @@ public boolean isInUse() { return (getFlags() & 0x01) != 0; } - + /** * Is this a directory? * @@ -164,6 +179,16 @@ } /** + * Gets the reference number of this record within the MFT. This value + * is not actually stored in the record, but passed in from the outside. + * + * @return the reference number. + */ + public long getReferenceNumber() { + return referenceNumber; + } + + /** * @return Returns the updateSequenceOffset. */ public int getUpdateSequenceOffset() { @@ -189,7 +214,7 @@ /** * Gets the name of this file. * - * @return + * @return the filename. */ public String getFileName() { final FileNameAttribute fnAttr = getFileNameAttribute(); @@ -203,7 +228,7 @@ /** * Gets the filename attribute of this filerecord. * - * @return + * @return the filename attribute. */ public FileNameAttribute getFileNameAttribute() { if (fileNameAttribute == null) { @@ -213,7 +238,7 @@ } /** - * Gets an attribute in this filerecord with a given id. + * Gets the attributes stored in this file record. * * @return an iteratover over attributes stored in this file record. */ @@ -328,6 +353,14 @@ * @throws IOException if an error occurs reading from the filesystem. */ public void readData(long fileOffset, byte[] dest, int off, int len) throws IOException { + if (log.isDebugEnabled()) { + log.debug("readData: offset " + fileOffset + " length " + len + ", file record = " + this); + } + + if (len == 0) { + return; + } + // Explicitly look for the attribute with no name, to avoid getting alternate streams. // XXX: Add API for getting length and content from alternate streams. final AttributeIterator dataAttrs = findAttributesByTypeAndName(NTFSAttribute.Types.DATA, null); @@ -348,11 +381,17 @@ "b) is not large enough to read:" + len + "b"); } resData.getData(resData.getAttributeOffset() + (int) fileOffset, dest, off, len); + + if (log.isDebugEnabled()) { + log.debug("readData: read from resident data"); + } + return; } + // At this point we know that at least the first attribute is non-resident. + // calculate start and end cluster - final int clusterSize = getVolume().getClusterSize(); final long startCluster = fileOffset / clusterSize; final long endCluster = (fileOffset + len - 1) / clusterSize; @@ -380,8 +419,13 @@ attr = dataAttrs.next(); } while (attr != null); + if (log.isDebugEnabled()) { + log.debug("readData: read " + readClusters + " from non-resident attributes"); + } + if (readClusters != nrClusters) { - throw new IOException("Requested " + nrClusters + " clusters but only read " + readClusters); + throw new IOException("Requested " + nrClusters + " clusters but only read " + readClusters + + ", file record = " + this); } System.arraycopy(tmp, (int) fileOffset % clusterSize, dest, off, len); Modified: trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSAttribute.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSAttribute.java 2008-12-14 21:49:21 UTC (rev 4788) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSAttribute.java 2008-12-15 08:40:42 UTC (rev 4789) @@ -189,7 +189,7 @@ } // check the resident flag - if (fileRecord.getUInt8(offset + 0x08) == 0) { + if (resident) { // resident return new NTFSResidentAttribute(fileRecord, offset); } else { Modified: trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSNonResidentAttribute.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSNonResidentAttribute.java 2008-12-14 21:49:21 UTC (rev 4788) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSNonResidentAttribute.java 2008-12-15 08:40:42 UTC (rev 4789) @@ -31,12 +31,13 @@ * * @author Chira * @author Ewout Prangsma (ep...@us...) + * @author Daniel Noll (da...@no...) (compression support) */ public class NTFSNonResidentAttribute extends NTFSAttribute { private int numberOfVCNs = 0; - private final List<DataRun> dataRuns = new ArrayList<DataRun>(); + private final List<DataRunInterface> dataRuns = new ArrayList<DataRunInterface>(); /** * @param fileRecord @@ -48,11 +49,6 @@ * process the dataruns...all non resident attributes have their data * outside. can find where using data runs */ - final int flags = getFlags(); - if (flags > 0) { - log.info("flags & 0x0001 = " + (flags & 0x0001)); - } - final int dataRunsOffset = getDataRunsOffset(); if (dataRunsOffset > 0) { readDataRuns(dataRunsOffset); @@ -86,6 +82,16 @@ } /** + * Gets the compression unit size. 2 to the power of this value is the number of clusters + * per compression unit. + * + * @return the compression unit size. + */ + public int getCompressionUnitSize() { + return getUInt16(0x22); + } + + /** * Gets the size allocated to the attribute. May be larger than the actual size of the * attribute data. * @@ -111,18 +117,55 @@ int offset = parentoffset; long previousLCN = 0; - final List<DataRun> dataruns = getDataRuns(); + final List<DataRunInterface> dataruns = getDataRuns(); long vcn = 0; + // If this attribute is compressed we will coalesce compressed/sparse + // data run pairs into a single data run object for convenience when reading. + boolean compressed = (getFlags() & 0x0001) != 0; + boolean expectingSparseRunNext = false; + int compUnitSize = 1 << getCompressionUnitSize(); + while (getUInt8(offset) != 0x0) { final DataRun dataRun = new DataRun(this, offset, vcn, previousLCN); - // map VCN-> datarun - dataruns.add(dataRun); - this.numberOfVCNs += dataRun.getLength(); + + if (compressed) { + if (dataRun.isSparse() && expectingSparseRunNext) { + // This is the sparse run which follows a compressed run. + // The number of runs it contains does not count towards the total + // as the compressed run reports holding all the runs for the pair. + // But we do need to move the offsets. Leaving this block open in case + // later it makes sense to put some logic in here. + } else if (dataRun.getLength() == compUnitSize) { + // Compressed/sparse pairs always add to the compression unit size. If + // the unit only compresses to 16, the system will store it uncompressed. + // So this whole unit is stored as-is, we'll leave it as a normal data run. + dataruns.add(dataRun); + this.numberOfVCNs += dataRun.getLength(); + vcn += dataRun.getLength(); + previousLCN = dataRun.getCluster(); + } else { + // TODO: Is it possible for the length to be GREATER than the unit size? + dataruns.add(new CompressedDataRun(dataRun, compUnitSize)); + if (dataRun.getLength() != compUnitSize) { + expectingSparseRunNext = true; + } + + this.numberOfVCNs += compUnitSize; + vcn += compUnitSize; + previousLCN = dataRun.getCluster(); + } + } else { + // map VCN-> datarun + dataruns.add(dataRun); + this.numberOfVCNs += dataRun.getLength(); + vcn += dataRun.getLength(); + previousLCN = dataRun.getCluster(); + } + offset += dataRun.getSize(); - previousLCN = dataRun.getCluster(); - vcn += dataRun.getLength(); } + // check the dataruns final int clusterSize = getFileRecord().getVolume().getClusterSize(); // Rounds up but won't work for 0, which shouldn't occur here. @@ -136,28 +179,13 @@ } /** - * @return Returns the dataRuns. + * @return Returns the data runs. */ - public List<DataRun> getDataRuns() { + private List<DataRunInterface> getDataRuns() { return dataRuns; } /** - * Appends extra data runs to this attribute, taken from another attribute. - * The starting VCN of the added runs are repositioned such the new runs line - * up with the end of the runs already contained in this attribute. - * - * @param dataRuns the data runs to append. - */ - public void appendDataRuns(List<DataRun> dataRuns) { - for (DataRun dataRun : dataRuns) { - dataRun.setFirstVcn(this.numberOfVCNs); - this.dataRuns.add(dataRun); - this.numberOfVCNs += dataRun.getLength(); - } - } - - /** * Read a number of clusters starting from a given virtual cluster number * (vcn). * @@ -167,16 +195,30 @@ * @throws IOException */ public int readVCN(long vcn, byte[] dst, int dstOffset, int nrClusters) throws IOException { + final int flags = getFlags(); + if ((flags & 0x4000) != 0) { + throw new IOException("Reading encrypted files is not supported"); + } + + if (log.isDebugEnabled()) { + log.debug("readVCN: wants start " + vcn + " length " + nrClusters + + ", we have start " + getStartVCN() + " length " + getNumberOfVCNs()); + } + final NTFSVolume volume = getFileRecord().getVolume(); final int clusterSize = volume.getClusterSize(); - int readClusters = 0; - for (DataRun dataRun : this.getDataRuns()) { + for (DataRunInterface dataRun : this.getDataRuns()) { readClusters += dataRun.readClusters(vcn, dst, dstOffset, nrClusters, clusterSize, volume); if (readClusters == nrClusters) { break; } } + + if (log.isDebugEnabled()) { + log.debug("readVCN: read " + readClusters); + } + return readClusters; } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ls...@us...> - 2009-01-05 15:09:07
|
Revision: 4830 http://jnode.svn.sourceforge.net/jnode/?rev=4830&view=rev Author: lsantha Date: 2009-01-05 15:09:00 +0000 (Mon, 05 Jan 2009) Log Message: ----------- Added read support for STANDARD_INFORMATION attribute, by Daniel Noll. Modified Paths: -------------- trunk/fs/src/fs/org/jnode/fs/ntfs/FileNameAttribute.java trunk/fs/src/fs/org/jnode/fs/ntfs/FileRecord.java trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSAttribute.java trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSEntry.java Added Paths: ----------- trunk/fs/src/fs/org/jnode/fs/ntfs/StandardInformationAttribute.java Modified: trunk/fs/src/fs/org/jnode/fs/ntfs/FileNameAttribute.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/FileNameAttribute.java 2009-01-04 16:37:04 UTC (rev 4829) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/FileNameAttribute.java 2009-01-05 15:09:00 UTC (rev 4830) @@ -128,6 +128,15 @@ } /** + * Gets the time when the MFT record last changed. + * + * @return the MFT change time, as a 64-bit NTFS filetime value. + */ + public long getMftChangeTime() { + return getInt64(getAttributeOffset() + 0x18); + } + + /** * Gets the access time. * * @return the access time, as a 64-bit NTFS filetime value. Modified: trunk/fs/src/fs/org/jnode/fs/ntfs/FileRecord.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/FileRecord.java 2009-01-04 16:37:04 UTC (rev 4829) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/FileRecord.java 2009-01-05 15:09:00 UTC (rev 4830) @@ -48,6 +48,11 @@ private AttributeListAttribute attributeListAttribute; /** + * Cached standard information attribute. + */ + private StandardInformationAttribute standardInformationAttribute; + + /** * Cached file name attribute. */ private FileNameAttribute fileNameAttribute; @@ -226,9 +231,22 @@ } /** - * Gets the filename attribute of this filerecord. + * Gets the standard information attribute for this file record. + * + * @return the standard information attribute. + */ + public StandardInformationAttribute getStandardInformationAttribute() { + if (standardInformationAttribute == null) { + standardInformationAttribute = + (StandardInformationAttribute) findAttributeByType(NTFSAttribute.Types.STANDARD_INFORMATION); + } + return standardInformationAttribute; + } + + /** + * Gets the file name attribute for this file record. * - * @return the filename attribute. + * @return the file name attribute. */ public FileNameAttribute getFileNameAttribute() { if (fileNameAttribute == null) { Modified: trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSAttribute.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSAttribute.java 2009-01-04 16:37:04 UTC (rev 4829) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSAttribute.java 2009-01-05 15:09:00 UTC (rev 4830) @@ -174,6 +174,8 @@ final int type = fileRecord.getUInt32AsInt(offset + 0x00); switch (type) { + case Types.STANDARD_INFORMATION: + return new StandardInformationAttribute(fileRecord, offset); case Types.ATTRIBUTE_LIST: if (resident) { return new AttributeListAttributeRes(fileRecord, offset); Modified: trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSEntry.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSEntry.java 2009-01-04 16:37:04 UTC (rev 4829) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSEntry.java 2009-01-05 15:09:00 UTC (rev 4830) @@ -70,18 +70,31 @@ } public long getCreated() throws IOException { - return NTFSUTIL.filetimeToMillis(getFileRecord().getFileNameAttribute() - .getCreationTime()); + final FileRecord record = getFileRecord(); + return NTFSUTIL.filetimeToMillis( + Math.max(record.getStandardInformationAttribute().getCreationTime(), + record.getFileNameAttribute().getCreationTime())); } public long getLastModified() throws IOException { - return NTFSUTIL.filetimeToMillis(getFileRecord().getFileNameAttribute() - .getModificationTime()); + final FileRecord record = getFileRecord(); + return NTFSUTIL.filetimeToMillis( + Math.max(record.getStandardInformationAttribute().getModificationTime(), + record.getFileNameAttribute().getModificationTime())); } + public long getLastChanged() throws IOException { + final FileRecord record = getFileRecord(); + return NTFSUTIL.filetimeToMillis( + Math.max(record.getStandardInformationAttribute().getMftChangeTime(), + record.getFileNameAttribute().getMftChangeTime())); + } + public long getLastAccessed() throws IOException { - return NTFSUTIL.filetimeToMillis(getFileRecord().getFileNameAttribute() - .getAccessTime()); + final FileRecord record = getFileRecord(); + return NTFSUTIL.filetimeToMillis( + Math.max(record.getStandardInformationAttribute().getAccessTime(), + record.getFileNameAttribute().getAccessTime())); } /** Added: trunk/fs/src/fs/org/jnode/fs/ntfs/StandardInformationAttribute.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/StandardInformationAttribute.java (rev 0) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/StandardInformationAttribute.java 2009-01-05 15:09:00 UTC (rev 4830) @@ -0,0 +1,64 @@ +package org.jnode.fs.ntfs; + +/** + * @author Daniel Noll (da...@no...) + */ +public class StandardInformationAttribute extends NTFSResidentAttribute { + + /** + * Constructs the attribute. + * + * @param fileRecord the containing file record. + * @param offset offset of the attribute within the file record. + */ + public StandardInformationAttribute(FileRecord fileRecord, int offset) { + super(fileRecord, offset); + } + + /** + * Gets the creation time. + * + * @return the creation time, as a 64-bit NTFS filetime value. + */ + public long getCreationTime() { + return getInt64(getAttributeOffset()); + } + + /** + * Gets the modification time. + * + * @return the modification time, as a 64-bit NTFS filetime value. + */ + public long getModificationTime() { + return getInt64(getAttributeOffset() + 0x08); + } + + /** + * Gets the time when the MFT record last changed. + * + * @return the MFT change time, as a 64-bit NTFS filetime value. + */ + public long getMftChangeTime() { + return getInt64(getAttributeOffset() + 0x10); + } + + /** + * Gets the access time. + * + * @return the access time, as a 64-bit NTFS filetime value. + */ + public long getAccessTime() { + return getInt64(getAttributeOffset() + 0x18); + } + + // TODO: The following fields have not yet been implemented due to no immediate need: + // offset bytes description + // 0x20 4 Flags + // 0x24 4 Maximum number of versions + // 0x28 4 Version number + // 0x2C 4 Class ID + // 0x30 4 Owner ID (version 3.0+) + // 0x34 4 Security ID (version 3.0+) + // 0x38 8 Quota charged (version 3.0+) + // 0x40 8 Update Sequence Number (USN) (version 3.0+) +} This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ga...@us...> - 2013-02-20 08:01:34
|
Revision: 5963 http://jnode.svn.sourceforge.net/jnode/?rev=5963&view=rev Author: galatnm Date: 2013-02-20 08:01:21 +0000 (Wed, 20 Feb 2013) Log Message: ----------- Move index related classes into a sub-package. (Luke Quinane) Modified Paths: -------------- trunk/fs/src/fs/org/jnode/fs/ntfs/CompressedDataRun.java trunk/fs/src/fs/org/jnode/fs/ntfs/DirectoryEntryIterator.java trunk/fs/src/fs/org/jnode/fs/ntfs/FileRecord.java trunk/fs/src/fs/org/jnode/fs/ntfs/MasterFileTable.java trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSAttribute.java trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSDirectory.java trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSEntry.java trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSFile.java trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSFileSystem.java trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSStructure.java trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSVolume.java Added Paths: ----------- trunk/fs/src/fs/org/jnode/fs/ntfs/index/ trunk/fs/src/fs/org/jnode/fs/ntfs/index/IndexAllocationAttribute.java trunk/fs/src/fs/org/jnode/fs/ntfs/index/IndexBlock.java trunk/fs/src/fs/org/jnode/fs/ntfs/index/IndexEntry.java trunk/fs/src/fs/org/jnode/fs/ntfs/index/IndexEntryIterator.java trunk/fs/src/fs/org/jnode/fs/ntfs/index/IndexHeader.java trunk/fs/src/fs/org/jnode/fs/ntfs/index/IndexRoot.java trunk/fs/src/fs/org/jnode/fs/ntfs/index/IndexRootAttribute.java trunk/fs/src/fs/org/jnode/fs/ntfs/index/NTFSIndex.java Removed Paths: ------------- trunk/fs/src/fs/org/jnode/fs/ntfs/IndexAllocationAttribute.java trunk/fs/src/fs/org/jnode/fs/ntfs/IndexBlock.java trunk/fs/src/fs/org/jnode/fs/ntfs/IndexEntry.java trunk/fs/src/fs/org/jnode/fs/ntfs/IndexEntryIterator.java trunk/fs/src/fs/org/jnode/fs/ntfs/IndexHeader.java trunk/fs/src/fs/org/jnode/fs/ntfs/IndexRoot.java trunk/fs/src/fs/org/jnode/fs/ntfs/IndexRootAttribute.java trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSIndex.java Modified: trunk/fs/src/fs/org/jnode/fs/ntfs/CompressedDataRun.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/CompressedDataRun.java 2013-02-19 16:26:07 UTC (rev 5962) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/CompressedDataRun.java 2013-02-20 08:01:21 UTC (rev 5963) @@ -22,6 +22,7 @@ import java.io.IOException; import java.util.Arrays; + import org.apache.log4j.Logger; import org.jnode.util.LittleEndian; Modified: trunk/fs/src/fs/org/jnode/fs/ntfs/DirectoryEntryIterator.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/DirectoryEntryIterator.java 2013-02-19 16:26:07 UTC (rev 5962) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/DirectoryEntryIterator.java 2013-02-20 08:01:21 UTC (rev 5963) @@ -23,6 +23,8 @@ import java.util.Iterator; import org.jnode.fs.FSEntry; +import org.jnode.fs.ntfs.index.IndexEntry; +import org.jnode.fs.ntfs.index.NTFSIndex; /** * Iterator for FSEntry's. @@ -51,8 +53,6 @@ /** * Are there more entries. - * - * @see org.jnode.fs.FSEntryIterator#hasNext() */ public boolean hasNext() { return (nextEntry != null); @@ -60,8 +60,6 @@ /** * Get the next entry. - * - * @see org.jnode.fs.FSEntryIterator#next() */ public FSEntry next() { final NTFSEntry result = nextEntry; Modified: trunk/fs/src/fs/org/jnode/fs/ntfs/FileRecord.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/FileRecord.java 2013-02-19 16:26:07 UTC (rev 5962) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/FileRecord.java 2013-02-20 08:01:21 UTC (rev 5963) @@ -24,6 +24,7 @@ import java.util.Collections; import java.util.Iterator; import java.util.List; + import org.jnode.util.NumberUtils; /** @@ -32,7 +33,7 @@ * @author Ewout Prangsma (ep...@us...) * @author Daniel Noll (da...@no...) (new attribute iteration support) */ -class FileRecord extends NTFSRecord { +public class FileRecord extends NTFSRecord { /** * Sequence number of the file within the MFT. Deleted: trunk/fs/src/fs/org/jnode/fs/ntfs/IndexAllocationAttribute.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/IndexAllocationAttribute.java 2013-02-19 16:26:07 UTC (rev 5962) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/IndexAllocationAttribute.java 2013-02-20 08:01:21 UTC (rev 5963) @@ -1,82 +0,0 @@ -/* - * $Id$ - * - * Copyright (C) 2003-2013 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.fs.ntfs; - -import java.io.IOException; - -/** - * @author Ewout Prangsma (ep...@us...) - */ -final class IndexAllocationAttribute extends NTFSNonResidentAttribute { - - /** - * Creates the index allocation attribute. - * - * @param fileRecord the file record. - * @param offset the offset of this attribute into the file record. - */ - public IndexAllocationAttribute(FileRecord fileRecord, int offset) { - super(fileRecord, offset); - } - - /** - * Gets the magic value of this record. - * - * @return the magic value. - */ - public int getMagic() { - return getUInt32AsInt(0x00); - } - - /** - * Read an index block starting at a given vcn. - * - * @param indexRoot - * @param vcn - * @return - * @throws IOException - */ - public IndexBlock getIndexBlock(IndexRoot indexRoot, long vcn) throws IOException { - log.debug("getIndexBlock(..," + vcn + ")"); - final FileRecord fileRecord = getFileRecord(); - - // VCN passed in is relative to the size of index clusters, not filesystem clusters. - // Calculate the actual offset we need in terms of filesystem clusters, - // and how many actual clusters we will need to read. - - final int indexBlockSize = indexRoot.getIndexBlockSize(); - final int indexClusterSize = indexBlockSize / indexRoot.getClustersPerIndexBlock(); - final int fsClusterSize = fileRecord.getVolume().getClusterSize(); - final long fsVcn = vcn * indexClusterSize / fsClusterSize; - final int fsNrClusters = (indexBlockSize - 1) / fsClusterSize + 1; - final int offsetIntoVcn = (int) ((vcn * indexClusterSize) % fsClusterSize); - - final byte[] data = new byte[fsNrClusters * fsClusterSize]; - final int readClusters = readVCN(fsVcn, data, 0, fsNrClusters); - if (readClusters != fsNrClusters) { - // If we don't throw an error now, it just fails more mysteriously later! - throw new IOException("Number of clusters read was not the number requested (requested " + - fsNrClusters + ", read " + readClusters + ")"); - } - - return new IndexBlock(fileRecord, data, offsetIntoVcn); - } -} Deleted: trunk/fs/src/fs/org/jnode/fs/ntfs/IndexBlock.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/IndexBlock.java 2013-02-19 16:26:07 UTC (rev 5962) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/IndexBlock.java 2013-02-20 08:01:21 UTC (rev 5963) @@ -1,96 +0,0 @@ -/* - * $Id$ - * - * Copyright (C) 2003-2013 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.fs.ntfs; - -import java.io.IOException; -import java.util.Iterator; - -/** - * @author Ewout Prangsma (ep...@us...) - */ -final class IndexBlock extends NTFSRecord { - - /** Cached header instance */ - private IndexHeader header; - - /** Parent file */ - private final FileRecord parentFileRecord; - - /** - * @param buffer - * @param offset - */ - public IndexBlock(FileRecord parentFileRecord, byte[] buffer, int offset) throws IOException { - super(parentFileRecord.getVolume(), buffer, offset); - this.parentFileRecord = parentFileRecord; - } - - /** - * $LogFile sequence number of the last modification of this index block. - * - * @return - */ - public long getLogFileSequenceNumber() { - return getInt64(0x08); - } - - /** - * Virtual cluster number of the index block. If the cluster_size on the - * volume is <= the index_block_size of the directory, index_block_vcn - * counts in units of clusters, and in units of sectors otherwise. - * - * @return - */ - public long getIndexBlockVCN() { - return getUInt32(0x10); - } - - /** - * Describes the following index entries. - * - * @return - */ - public IndexHeader getHeader() { - if (header == null) { - header = new IndexHeader(this, 0x18); - } - return header; - } - - /** - * Gets the parent file record. - * - * @return - */ - public FileRecord getParentFileRecord() { - return parentFileRecord; - } - - /** - * Gets an iterator to iterate over all IndexEntry's in this block. - * - * @return - */ - public Iterator<IndexEntry> iterator() { - return new IndexEntryIterator(parentFileRecord, this, - getHeader().getFirstEntryOffset() + 0x18); - } -} Deleted: trunk/fs/src/fs/org/jnode/fs/ntfs/IndexEntry.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/IndexEntry.java 2013-02-19 16:26:07 UTC (rev 5962) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/IndexEntry.java 2013-02-20 08:01:21 UTC (rev 5963) @@ -1,138 +0,0 @@ -/* - * $Id$ - * - * Copyright (C) 2003-2013 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.fs.ntfs; - -import java.io.UnsupportedEncodingException; - -/** - * Structure accessor of an Index Entry. - * @author vali - * @author Ewout Prangsma (ep...@us...) - */ -public final class IndexEntry extends NTFSStructure { - - private final FileRecord parentFileRecord; - - /** - * Initialize this instance. - * @param parentFileRecord - * @param buffer - * @param offset - */ - public IndexEntry(FileRecord parentFileRecord, byte[] buffer, int offset) { - super(buffer, offset); - this.parentFileRecord = parentFileRecord; - } - - /** - * Initialize this instance. - * @param parentFileRecord - * @param parent - * @param offset - */ - public IndexEntry(FileRecord parentFileRecord, NTFSStructure parent, int offset) { - super(parent, offset); - this.parentFileRecord = parentFileRecord; - } - - public boolean hasSubNodes() { - return (getIndexFlags() & 0x01) != 0; - } - - /** - * Gets the length of this index entry in bytes. - * @return - */ - public int getSize() { - return getUInt16(0x08); - } - - /** - * Gets the flags of this index entry. - * @return - */ - public int getIndexFlags() { - return getUInt8(0x0C); - } - - public boolean isLastIndexEntryInSubnode() { - return (getIndexFlags() & 0x02) != 0; - } - - /** - * Gets the filename namespace. - * @see FileNameAttribute.NameSpace - * @return - */ - public int getNameSpace() { - return getUInt8(0x51); - } - - public boolean isDirectory() { - return (getFileFlags() & 0x10000000L) != 0; - } - - public String getFileName() { - try { - // XXX: For Java 6, should use the version that accepts a Charset. - return new String(this.getFileNameAsByteArray(), "UTF-16LE"); - } catch (UnsupportedEncodingException e) { - throw new IllegalStateException("UTF-16LE charset missing from JRE", e); - } - } - - public long getFileReferenceNumber() { - return getUInt48(0x00); - } - - public long getFileFlags() { - return getInt64(0x48); - } - - public long getRealFileSize() { - return getInt64(0x40); - } - - /** - * @return Returns the parentFileRecord. - */ - public FileRecord getParentFileRecord() { - return parentFileRecord; - } - - public long getSubnodeVCN() { - return getInt64(getSize() - 8); // TODO: getUInt64AsInt - // return getUInt32(getSize() - 8); - } - - private byte[] getFileNameAsByteArray() { - final int len = getUInt8(0x50); - final byte[] bytes = new byte[len * 2]; - getData(0x52, bytes, 0, bytes.length); - return bytes; - } - - @Override - public String toString() { - return super.toString() + "[fileName=" + getFileName() + ",indexFlags=" + getIndexFlags() + ",fileFlags=" - + getFileFlags() + "]"; - } -} Deleted: trunk/fs/src/fs/org/jnode/fs/ntfs/IndexEntryIterator.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/IndexEntryIterator.java 2013-02-19 16:26:07 UTC (rev 5962) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/IndexEntryIterator.java 2013-02-20 08:01:21 UTC (rev 5963) @@ -1,94 +0,0 @@ -/* - * $Id$ - * - * Copyright (C) 2003-2013 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.fs.ntfs; - -import java.util.Iterator; -import java.util.NoSuchElementException; - -/** - * Iterator used to iterate over all IndexEntry's in an index block or index_root attribute. - * @author Ewout Prangsma (ep...@us...) - */ -public final class IndexEntryIterator implements Iterator<IndexEntry> { - private int offset; - private IndexEntry nextEntry; - private final NTFSStructure parent; - private final FileRecord parentFileRecord; - - /** - * Initialize this instance. - */ - public IndexEntryIterator(FileRecord parentFileRecord, NTFSStructure parent, int firstOffset) { - this.offset = firstOffset; - this.parentFileRecord = parentFileRecord; - this.parent = parent; - readNext(); - } - - /** - * @see java.util.Iterator#hasNext() - */ - public boolean hasNext() { - if (nextEntry == null) { - return false; - } - return !nextEntry.isLastIndexEntryInSubnode() || nextEntry.hasSubNodes(); - } - - /** - * @see java.util.Iterator#next() - */ - public IndexEntry next() { - if (nextEntry == null) { - throw new NoSuchElementException(); - } else { - final IndexEntry result = nextEntry; - final int size = nextEntry.getSize(); - - // Prevents an infinite loop. Seen on images where the size of the index entry has been zeroed out. - if (size <= 0) { - throw new IllegalStateException(String.format( - "Index entry size is 0, filesystem is corrupt. Parent directory: '%s', reference number '%d'", - nextEntry.getParentFileRecord().getFileName(), - nextEntry.getParentFileRecord().getReferenceNumber())); - } - - offset += size; - // Now read the next next entry - readNext(); - return result; - } - } - - /** - * @see java.util.Iterator#remove() - */ - public void remove() { - throw new UnsupportedOperationException(); - } - - private void readNext() { - nextEntry = new IndexEntry(parentFileRecord, parent, offset); - if (nextEntry.isLastIndexEntryInSubnode() && !nextEntry.hasSubNodes()) { - nextEntry = null; - } - } -} Deleted: trunk/fs/src/fs/org/jnode/fs/ntfs/IndexHeader.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/IndexHeader.java 2013-02-19 16:26:07 UTC (rev 5962) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/IndexHeader.java 2013-02-20 08:01:21 UTC (rev 5963) @@ -1,92 +0,0 @@ -/* - * $Id$ - * - * Copyright (C) 2003-2013 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.fs.ntfs; - - -/** - * @author Ewout Prangsma (ep...@us...) - */ -final class IndexHeader extends NTFSStructure { - - /** Size of this structure index bytes */ - public static final int SIZE = 0x10; - - public static final class Flags { - public static final int LARGE_INDEX = 0x01; - } - - /** - * Initialize this instance. - * @param attr - */ - public IndexHeader(IndexRootAttribute attr) { - super(attr, attr.getAttributeOffset() + 0x10); - } - - /** - * Initialize this instance. - * @param indexBlock - * @param offset - */ - public IndexHeader(IndexBlock indexBlock, int offset) { - super(indexBlock, offset); - } - - /** - * Gets the offset of the first index entry. - * @return - */ - public int getFirstEntryOffset() { - return getUInt32AsInt(0x00); - } - - /** - * Gets the total size of the index entries (in bytes???). - * @return - */ - public int getIndexEntriesSize() { - return getUInt32AsInt(0x04); - } - - /** - * Gets the allocated size of the index entries (in bytes???). - * @return - */ - public int getAllocatedIndexEntriesSize() { - return getUInt32AsInt(0x08); - } - - /** - * Gets the flags. - * @return - */ - public int getFlags() { - return getUInt8(0x0C); - } - - /** - * Is an index allocation needed. - * @return - */ - public boolean isLargeIndex() { - return ((getFlags() & Flags.LARGE_INDEX) != 0); - } -} Deleted: trunk/fs/src/fs/org/jnode/fs/ntfs/IndexRoot.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/IndexRoot.java 2013-02-19 16:26:07 UTC (rev 5962) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/IndexRoot.java 2013-02-20 08:01:21 UTC (rev 5963) @@ -1,75 +0,0 @@ -/* - * $Id$ - * - * Copyright (C) 2003-2013 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.fs.ntfs; - - -/** - * @author Ewout Prangsma (ep...@us...) - */ -final class IndexRoot extends NTFSStructure { - - public static final int SIZE = 0x10; - - /** - * Initialize this instance. - * @param attr - */ - public IndexRoot(IndexRootAttribute attr) { - super(attr, attr.getAttributeOffset()); - } - - /** - * Gets the attribute type. - * @return - */ - public int getAttributeType() { - return getUInt32AsInt(0x00); - } - - /** - * Gets the collation rule. - * @return - */ - public int getCollationRule() { - return getUInt32AsInt(0x04); - } - - /** - * Size of each index block in bytes (in the index allocation attribute). - * @return - */ - public int getIndexBlockSize() { - return getUInt32AsInt(0x08); - } - - /** - * Gets the number of clusters per index record. - * @return - */ - public int getClustersPerIndexBlock() { - final int v = getInt8(0x0C); - if (v < 0) { - return 1; - } else { - return v; - } - } -} Deleted: trunk/fs/src/fs/org/jnode/fs/ntfs/IndexRootAttribute.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/IndexRootAttribute.java 2013-02-19 16:26:07 UTC (rev 5962) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/IndexRootAttribute.java 2013-02-20 08:01:21 UTC (rev 5963) @@ -1,72 +0,0 @@ -/* - * $Id$ - * - * Copyright (C) 2003-2013 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.fs.ntfs; - -import java.util.Iterator; - -/** - * @author Ewout Prangsma (ep...@us...) - */ -public final class IndexRootAttribute extends NTFSResidentAttribute { - - private IndexRoot root; - - private IndexHeader header; - - /** - * @param fileRecord - * @param offset - */ - public IndexRootAttribute(FileRecord fileRecord, int offset) { - super(fileRecord, offset); - } - - /** - * Gets the index root structure. - * @return - */ - public IndexRoot getRoot() { - if (root == null) { - root = new IndexRoot(this); - } - return root; - } - - /** - * Gets the index header structure. - * @return - */ - public IndexHeader getHeader() { - if (header == null) { - header = new IndexHeader(this); - } - return header; - } - - /** - * Gets an iterator to iterate over all IndexEntry's in this index_root attribute. - * @return - */ - public Iterator<IndexEntry> iterator() { - final int headerOffset = getAttributeOffset() + IndexRoot.SIZE; - return new IndexEntryIterator(getFileRecord(), this, headerOffset + getHeader().getFirstEntryOffset()); - } -} Modified: trunk/fs/src/fs/org/jnode/fs/ntfs/MasterFileTable.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/MasterFileTable.java 2013-02-19 16:26:07 UTC (rev 5962) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/MasterFileTable.java 2013-02-20 08:01:21 UTC (rev 5963) @@ -22,6 +22,8 @@ import java.io.IOException; +import org.jnode.fs.ntfs.index.IndexEntry; + /** * @author Ewout Prangsma (ep...@us...) */ Modified: trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSAttribute.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSAttribute.java 2013-02-19 16:26:07 UTC (rev 5962) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSAttribute.java 2013-02-20 08:01:21 UTC (rev 5963) @@ -20,6 +20,9 @@ package org.jnode.fs.ntfs; +import org.jnode.fs.ntfs.index.IndexAllocationAttribute; +import org.jnode.fs.ntfs.index.IndexRootAttribute; + /** * @author Chira * @author Ewout Prangsma (ep...@us...) Modified: trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSDirectory.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSDirectory.java 2013-02-19 16:26:07 UTC (rev 5962) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSDirectory.java 2013-02-20 08:01:21 UTC (rev 5963) @@ -28,6 +28,7 @@ import org.jnode.fs.FSEntry; import org.jnode.fs.FileSystem; import org.jnode.fs.ReadOnlyFileSystemException; +import org.jnode.fs.ntfs.index.NTFSIndex; /** * @author vali Modified: trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSEntry.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSEntry.java 2013-02-19 16:26:07 UTC (rev 5962) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSEntry.java 2013-02-20 08:01:21 UTC (rev 5963) @@ -21,6 +21,7 @@ package org.jnode.fs.ntfs; import java.io.IOException; + import org.jnode.fs.FSAccessRights; import org.jnode.fs.FSDirectory; import org.jnode.fs.FSEntry; @@ -30,6 +31,7 @@ import org.jnode.fs.FSFile; import org.jnode.fs.FSObject; import org.jnode.fs.FileSystem; +import org.jnode.fs.ntfs.index.IndexEntry; /** * @author vali Modified: trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSFile.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSFile.java 2013-02-19 16:26:07 UTC (rev 5962) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSFile.java 2013-02-20 08:01:21 UTC (rev 5963) @@ -22,9 +22,11 @@ import java.io.IOException; import java.nio.ByteBuffer; + import org.jnode.fs.FSFile; import org.jnode.fs.FSFileSlackSpace; import org.jnode.fs.FileSystem; +import org.jnode.fs.ntfs.index.IndexEntry; import org.jnode.util.ByteBufferUtils; /** Modified: trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSFileSystem.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSFileSystem.java 2013-02-19 16:26:07 UTC (rev 5962) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSFileSystem.java 2013-02-20 08:01:21 UTC (rev 5963) @@ -22,6 +22,7 @@ import java.io.IOException; import java.io.UnsupportedEncodingException; + import org.jnode.driver.Device; import org.jnode.fs.FSDirectory; import org.jnode.fs.FSEntry; Deleted: trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSIndex.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSIndex.java 2013-02-19 16:26:07 UTC (rev 5962) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSIndex.java 2013-02-20 08:01:21 UTC (rev 5963) @@ -1,174 +0,0 @@ -/* - * $Id$ - * - * Copyright (C) 2003-2013 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.fs.ntfs; - -import java.io.IOException; -import java.util.Iterator; -import java.util.NoSuchElementException; - -import org.apache.log4j.Logger; -import org.jnode.util.Queue; - -/** - * @author Chira - * @author Ewout Prangsma (ep...@us...) - */ -final class NTFSIndex { - - private final FileRecord fileRecord; - - private IndexRootAttribute indexRootAttribute; - - private IndexAllocationAttribute indexAllocationAttribute; - - static final Logger log = Logger.getLogger(NTFSIndex.class); - - /** - * Initialize this instance. - * - * @param fileRecord - */ - public NTFSIndex(FileRecord fileRecord) throws IOException { - this.fileRecord = fileRecord; - if (!fileRecord.isDirectory()) { - throw new IOException("fileRecord is not a directory"); - } - } - - /** - * Gets the index root attribute. - * - * @return - */ - public IndexRootAttribute getIndexRootAttribute() { - if (indexRootAttribute == null) { - indexRootAttribute = (IndexRootAttribute) - fileRecord.findAttributeByType(NTFSAttribute.Types.INDEX_ROOT); - log.debug("getIndexRootAttribute: " + indexRootAttribute); - } - return indexRootAttribute; - } - - /** - * Gets the index allocation attribute, if any. - * - * @return - */ - public IndexAllocationAttribute getIndexAllocationAttribute() { - if (indexAllocationAttribute == null) { - indexAllocationAttribute = (IndexAllocationAttribute) - fileRecord.findAttributeByType(NTFSAttribute.Types.INDEX_ALLOCATION); - } - return indexAllocationAttribute; - } - - public Iterator<IndexEntry> iterator() { - log.debug("iterator"); - return new FullIndexEntryIterator(); - } - - class FullIndexEntryIterator implements Iterator<IndexEntry> { - - /** - * List of those IndexEntry's that have a subnode and the subnode has - * not been visited. - */ - private final Queue<IndexEntry> subNodeEntries = new Queue<IndexEntry>(); - - /** Iterator of current part of the index */ - private Iterator<IndexEntry> currentIterator; - - private IndexEntry nextEntry; - - /** - * Initialize this instance. - */ - public FullIndexEntryIterator() { - log.debug("FullIndexEntryIterator"); - currentIterator = getIndexRootAttribute().iterator(); - log.debug("currentIterator=" + currentIterator); - readNextEntry(); - } - - /** - * @see java.util.Iterator#hasNext() - */ - public boolean hasNext() { - return (nextEntry != null); - } - - /** - * @see java.util.Iterator#next() - */ - public IndexEntry next() { - final IndexEntry result = nextEntry; - if (result == null) { - throw new NoSuchElementException(); - } - readNextEntry(); - return result; - } - - /** - * @see java.util.Iterator#remove() - */ - public void remove() { - throw new UnsupportedOperationException(); - } - - private void readNextEntry() { - while (true) { - if (currentIterator.hasNext()) { - // Read it - nextEntry = currentIterator.next(); - if (nextEntry.hasSubNodes()) { - log.debug("next has subnode"); - subNodeEntries.add(nextEntry); - } - if (!nextEntry.isLastIndexEntryInSubnode()) { - return; - } - } - nextEntry = null; - - // Do we have subnodes to iterate over? - if (subNodeEntries.isEmpty()) { - // No, we're done - log.debug("end of list"); - return; - } - - log.debug("hasNext: read next indexblock"); - final IndexEntry entry = subNodeEntries.get(); - final IndexRoot indexRoot = getIndexRootAttribute().getRoot(); - final IndexBlock indexBlock; - try { - indexBlock = getIndexAllocationAttribute().getIndexBlock( - indexRoot, entry.getSubnodeVCN()); - } catch (IOException ex) { - log.error("Cannot read next index block", ex); - return; - } - currentIterator = indexBlock.iterator(); - } - } - } -} Modified: trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSStructure.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSStructure.java 2013-02-19 16:26:07 UTC (rev 5962) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSStructure.java 2013-02-20 08:01:21 UTC (rev 5963) @@ -26,7 +26,7 @@ /** * @author Ewout Prangsma (ep...@us...) */ -abstract class NTFSStructure { +public abstract class NTFSStructure { /** My logger */ protected static final Logger log = Logger.getLogger(NTFSStructure.class); @@ -39,7 +39,7 @@ * @param buffer * @param offset */ - NTFSStructure(byte[] buffer, int offset) { + public NTFSStructure(byte[] buffer, int offset) { this.buffer = buffer; this.offset = offset; } @@ -49,7 +49,7 @@ * @param parent * @param offset */ - NTFSStructure(NTFSStructure parent, int offset) { + public NTFSStructure(NTFSStructure parent, int offset) { this.buffer = parent.buffer; this.offset = parent.offset + offset; } @@ -79,7 +79,7 @@ * @param offset * @return */ - final int getUInt8(int offset) { + public final int getUInt8(int offset) { return LittleEndian.getUInt8(buffer, this.offset + offset); } @@ -88,7 +88,7 @@ * @param offset * @return */ - final int getUInt16(int offset) { + public final int getUInt16(int offset) { return LittleEndian.getUInt16(buffer, this.offset + offset); } @@ -97,7 +97,7 @@ * @param offset * @return */ - final int getUInt24(int offset) { + public final int getUInt24(int offset) { return LittleEndian.getUInt24(buffer, this.offset + offset); } @@ -106,7 +106,7 @@ * @param offset * @return */ - final long getUInt32(int offset) { + public final long getUInt32(int offset) { return LittleEndian.getUInt32(buffer, this.offset + offset); } @@ -115,7 +115,7 @@ * @param offset * @return */ - final int getUInt32AsInt(int offset) { + public final int getUInt32AsInt(int offset) { return (int) LittleEndian.getUInt32(buffer, this.offset + offset); } @@ -124,7 +124,7 @@ * @param offset * @return */ - final long getUInt48(int offset) { + public final long getUInt48(int offset) { return LittleEndian.getUInt48(buffer, this.offset + offset); } @@ -133,7 +133,7 @@ * @param offset * @return */ - final int getInt8(int offset) { + public final int getInt8(int offset) { return LittleEndian.getInt8(buffer, this.offset + offset); } @@ -142,7 +142,7 @@ * @param offset * @return */ - final int getInt16(int offset) { + public final int getInt16(int offset) { return LittleEndian.getInt16(buffer, this.offset + offset); } @@ -151,7 +151,7 @@ * @param offset * @return */ - final int getInt24(int offset) { + public final int getInt24(int offset) { return LittleEndian.getInt24(buffer, this.offset + offset); } @@ -160,7 +160,7 @@ * @param offset * @return */ - final int getInt32(int offset) { + public final int getInt32(int offset) { return LittleEndian.getInt32(buffer, this.offset + offset); } @@ -169,7 +169,7 @@ * @param offset * @return */ - final long getInt64(int offset) { + public final long getInt64(int offset) { return LittleEndian.getInt64(buffer, this.offset + offset); } @@ -189,7 +189,7 @@ * @param offset * @return */ - final char getChar16(int offset) { + public final char getChar16(int offset) { final int v0 = buffer[this.offset + offset] & 0xFF; final int v1 = buffer[this.offset + offset + 1] & 0xFF; return (char) ((v1 << 8) | v0); @@ -199,7 +199,7 @@ * Write an unsigned 16-bit integer to a given offset. * @param offset */ - final void setUInt16(int offset, int value) { + public final void setUInt16(int offset, int value) { LittleEndian.setInt16(buffer, this.offset + offset, value); } Modified: trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSVolume.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSVolume.java 2013-02-19 16:26:07 UTC (rev 5962) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSVolume.java 2013-02-20 08:01:21 UTC (rev 5963) @@ -22,6 +22,7 @@ import java.io.IOException; import java.nio.ByteBuffer; + import org.apache.log4j.Logger; import org.jnode.driver.block.BlockDeviceAPI; Added: trunk/fs/src/fs/org/jnode/fs/ntfs/index/IndexAllocationAttribute.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/index/IndexAllocationAttribute.java (rev 0) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/index/IndexAllocationAttribute.java 2013-02-20 08:01:21 UTC (rev 5963) @@ -0,0 +1,85 @@ +/* + * $Id$ + * + * Copyright (C) 2003-2012 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.fs.ntfs.index; + +import java.io.IOException; + +import org.jnode.fs.ntfs.FileRecord; +import org.jnode.fs.ntfs.NTFSNonResidentAttribute; + +/** + * @author Ewout Prangsma (ep...@us...) + */ +public final class IndexAllocationAttribute extends NTFSNonResidentAttribute { + + /** + * Creates the index allocation attribute. + * + * @param fileRecord the file record. + * @param offset the offset of this attribute into the file record. + */ + public IndexAllocationAttribute(FileRecord fileRecord, int offset) { + super(fileRecord, offset); + } + + /** + * Gets the magic value of this record. + * + * @return the magic value. + */ + public int getMagic() { + return getUInt32AsInt(0x00); + } + + /** + * Read an index block starting at a given vcn. + * + * @param indexRoot + * @param vcn + * @return + * @throws IOException + */ + public IndexBlock getIndexBlock(IndexRoot indexRoot, long vcn) throws IOException { + log.debug("getIndexBlock(..," + vcn + ")"); + final FileRecord fileRecord = getFileRecord(); + + // VCN passed in is relative to the size of index clusters, not filesystem clusters. + // Calculate the actual offset we need in terms of filesystem clusters, + // and how many actual clusters we will need to read. + + final int indexBlockSize = indexRoot.getIndexBlockSize(); + final int indexClusterSize = indexBlockSize / indexRoot.getClustersPerIndexBlock(); + final int fsClusterSize = fileRecord.getVolume().getClusterSize(); + final long fsVcn = vcn * indexClusterSize / fsClusterSize; + final int fsNrClusters = (indexBlockSize - 1) / fsClusterSize + 1; + final int offsetIntoVcn = (int) ((vcn * indexClusterSize) % fsClusterSize); + + final byte[] data = new byte[fsNrClusters * fsClusterSize]; + final int readClusters = readVCN(fsVcn, data, 0, fsNrClusters); + if (readClusters != fsNrClusters) { + // If we don't throw an error now, it just fails more mysteriously later! + throw new IOException("Number of clusters read was not the number requested (requested " + + fsNrClusters + ", read " + readClusters + ")"); + } + + return new IndexBlock(fileRecord, data, offsetIntoVcn); + } +} Added: trunk/fs/src/fs/org/jnode/fs/ntfs/index/IndexBlock.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/index/IndexBlock.java (rev 0) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/index/IndexBlock.java 2013-02-20 08:01:21 UTC (rev 5963) @@ -0,0 +1,99 @@ +/* + * $Id$ + * + * Copyright (C) 2003-2012 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.fs.ntfs.index; + +import java.io.IOException; +import java.util.Iterator; + +import org.jnode.fs.ntfs.FileRecord; +import org.jnode.fs.ntfs.NTFSRecord; + +/** + * @author Ewout Prangsma (ep...@us...) + */ +final class IndexBlock extends NTFSRecord { + + /** Cached header instance */ + private IndexHeader header; + + /** Parent file */ + private final FileRecord parentFileRecord; + + /** + * @param buffer + * @param offset + */ + public IndexBlock(FileRecord parentFileRecord, byte[] buffer, int offset) throws IOException { + super(parentFileRecord.getVolume(), buffer, offset); + this.parentFileRecord = parentFileRecord; + } + + /** + * $LogFile sequence number of the last modification of this index block. + * + * @return + */ + public long getLogFileSequenceNumber() { + return getInt64(0x08); + } + + /** + * Virtual cluster number of the index block. If the cluster_size on the + * volume is <= the index_block_size of the directory, index_block_vcn + * counts in units of clusters, and in units of sectors otherwise. + * + * @return + */ + public long getIndexBlockVCN() { + return getUInt32(0x10); + } + + /** + * Describes the following index entries. + * + * @return + */ + public IndexHeader getHeader() { + if (header == null) { + header = new IndexHeader(this, 0x18); + } + return header; + } + + /** + * Gets the parent file record. + * + * @return + */ + public FileRecord getParentFileRecord() { + return parentFileRecord; + } + + /** + * Gets an iterator to iterate over all IndexEntry's in this block. + * + * @return + */ + public Iterator<IndexEntry> iterator() { + return new IndexEntryIterator(parentFileRecord, this, + getHeader().getFirstEntryOffset() + 0x18); + } +} Added: trunk/fs/src/fs/org/jnode/fs/ntfs/index/IndexEntry.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/index/IndexEntry.java (rev 0) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/index/IndexEntry.java 2013-02-20 08:01:21 UTC (rev 5963) @@ -0,0 +1,144 @@ +/* + * $Id$ + * + * Copyright (C) 2003-2012 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.fs.ntfs.index; + +import java.io.UnsupportedEncodingException; + +import org.jnode.fs.ntfs.FileRecord; +import org.jnode.fs.ntfs.NTFSStructure; + +/** + * Structure accessor of an Index Entry. + * + * @author vali + * @author Ewout Prangsma (ep...@us...) + */ +public final class IndexEntry extends NTFSStructure { + + private final FileRecord parentFileRecord; + + /** + * Initialize this instance. + * @param parentFileRecord + * @param buffer + * @param offset + */ + public IndexEntry(FileRecord parentFileRecord, byte[] buffer, int offset) { + super(buffer, offset); + this.parentFileRecord = parentFileRecord; + } + + /** + * Initialize this instance. + * @param parentFileRecord + * @param parent + * @param offset + */ + public IndexEntry(FileRecord parentFileRecord, NTFSStructure parent, int offset) { + super(parent, offset); + this.parentFileRecord = parentFileRecord; + } + + public boolean hasSubNodes() { + return (getIndexFlags() & 0x01) != 0; + } + + /** + * Gets the length of this index entry in bytes. + * @return + */ + public int getSize() { + return getUInt16(0x08); + } + + /** + * Gets the flags of this index entry. + * @return + */ + public int getIndexFlags() { + return getUInt8(0x0C); + } + + public boolean isLastIndexEntryInSubnode() { + return (getIndexFlags() & 0x02) != 0; + } + + /** + * Gets the filename namespace. + * @see org.jnode.fs.ntfs.FileNameAttribute.NameSpace + * @return + */ + public int getNameSpace() { + return getUInt8(0x51); + } + + public boolean isDirectory() { + return (getFileFlags() & 0x10000000L) != 0; + } + + public String getFileName() { + try { + //XXX: For Java 6, should use the version that accepts a Charset. + return new String(this.getFileNameAsByteArray(), "UTF-16LE"); + } catch (UnsupportedEncodingException e) { + throw new IllegalStateException("UTF-16LE charset missing from JRE", e); + } + } + + public long getFileReferenceNumber() { + return getUInt48(0x00); + } + + public long getFileFlags() { + return getInt64(0x48); + } + + public long getRealFileSize() { + return getInt64(0x40); + } + + /** + * @return Returns the parentFileRecord. + */ + public FileRecord getParentFileRecord() { + return parentFileRecord; + } + + public long getSubnodeVCN() { + return getInt64(getSize() - 8); // TODO: getUInt64AsInt + //return getUInt32(getSize() - 8); + } + + private byte[] getFileNameAsByteArray() { + final int len = getUInt8(0x50); + final byte[] bytes = new byte[len * 2]; + getData(0x52, bytes, 0, bytes.length); + return bytes; + } + + @Override + public String toString() { + return super.toString() + + "[fileName=" + getFileName() + + ",indexFlags=" + getIndexFlags() + + ",fileFlags=" + getFileFlags() + "]"; + } +} Added: trunk/fs/src/fs/org/jnode/fs/ntfs/index/IndexEntryIterator.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/index/IndexEntryIterator.java (rev 0) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/index/IndexEntryIterator.java 2013-02-20 08:01:21 UTC (rev 5963) @@ -0,0 +1,100 @@ +/* + * $Id$ + * + * Copyright (C) 2003-2012 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.fs.ntfs.index; + +import java.util.Iterator; +import java.util.NoSuchElementException; + +import org.jnode.fs.ntfs.FileRecord; +import org.jnode.fs.ntfs.NTFSStructure; + + +/** + * Iterator used to iterate over all IndexEntry's in an index block + * or index_root attribute. + * + * @author Ewout Prangsma (ep...@us...) + */ +public final class IndexEntryIterator implements Iterator<IndexEntry> { + private int offset; + private IndexEntry nextEntry; + private final NTFSStructure parent; + private final FileRecord parentFileRecord; + + /** + * Initialize this instance. + */ + public IndexEntryIterator(FileRecord parentFileRecord, NTFSStructure parent, int firstOffset) { + this.offset = firstOffset; + this.parentFileRecord = parentFileRecord; + this.parent = parent; + readNext(); + } + + /** + * @see java.util.Iterator#hasNext() + */ + public boolean hasNext() { + if (nextEntry == null) { + return false; + } + return !nextEntry.isLastIndexEntryInSubnode() || nextEntry.hasSubNodes(); + } + + /** + * @see java.util.Iterator#next() + */ + public IndexEntry next() { + if (nextEntry == null) { + throw new NoSuchElementException(); + } else { + final IndexEntry result = nextEntry; + final int size = nextEntry.getSize(); + + // Prevents an infinite loop. Seen on images where the size of the index entry has been zeroed out. + if (size <= 0) { + throw new IllegalStateException(String.format( + "Index entry size is 0, filesystem is corrupt. Parent directory: '%s', reference number '%d'", + nextEntry.getParentFileRecord().getFileName(), + nextEntry.getParentFileRecord().getReferenceNumber())); + } + + offset += size; + // Now read the next next entry + readNext(); + return result; + } + } + + /** + * @see java.util.Iterator#remove() + */ + public void remove() { + throw new UnsupportedOperationException(); + } + + private void readNext() { + nextEntry = new IndexEntry(parentFileRecord, parent, offset); + if (nextEntry.isLastIndexEntryInSubnode() && !nextEntry.hasSubNodes()) { + nextEntry = null; + } + } +} Added: trunk/fs/src/fs/org/jnode/fs/ntfs/index/IndexHeader.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/index/IndexHeader.java (rev 0) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/index/IndexHeader.java 2013-02-20 08:01:21 UTC (rev 5963) @@ -0,0 +1,95 @@ +/* + * $Id$ + * + * Copyright (C) 2003-2012 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.fs.ntfs.index; + +import org.jnode.fs.ntfs.NTFSStructure; + + + +/** + * @author Ewout Prangsma (ep...@us...) + */ +final class IndexHeader extends NTFSStructure { + + /** Size of this structure index bytes */ + public static final int SIZE = 0x10; + + public static final class Flags { + public static final int LARGE_INDEX = 0x01; + } + + /** + * Initialize this instance. + * @param attr + */ + public IndexHeader(IndexRootAttribute attr) { + super(attr, attr.getAttributeOffset() + 0x10); + } + + /** + * Initialize this instance. + * @param indexBlock + * @param offset + */ + public IndexHeader(IndexBlock indexBlock, int offset) { + super(indexBlock, offset); + } + + /** + * Gets the offset of the first index entry. + * @return + */ + public int getFirstEntryOffset() { + return getUInt32AsInt(0x00); + } + + /** + * Gets the total size of the index entries (in bytes???). + * @return + */ + public int getIndexEntriesSize() { + return getUInt32AsInt(0x04); + } + + /** + * Gets the allocated size of the index entries (in bytes???). + * @return + */ + public int getAllocatedIndexEntriesSize() { + return getUInt32AsInt(0x08); + } + + /** + * Gets the flags. + * @return + */ + public int getFlags() { + return getUInt8(0x0C); + } + + /** + * Is an index allocation needed. + * @return + */ + public boolean isLargeIndex() { + return ((getFlags() & Flags.LARGE_INDEX) != 0); + } +} Added: trunk/fs/src/fs/org/jnode/fs/ntfs/index/IndexRoot.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/index/IndexRoot.java (rev 0) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/index/IndexRoot.java 2013-02-20 08:01:21 UTC (rev 5963) @@ -0,0 +1,78 @@ +/* + * $Id$ + * + * Copyright (C) 2003-2012 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.fs.ntfs.index; + +import org.jnode.fs.ntfs.NTFSStructure; + + + +/** + * @author Ewout Prangsma (ep...@us...) + */ +final class IndexRoot extends NTFSStructure { + + public static final int SIZE = 0x10; + + /** + * Initialize this instance. + * @param attr + */ + public IndexRoot(IndexRootAttribute attr) { + super(attr, attr.getAttributeOffset()); + } + + /** + * Gets the attribute type. + * @return + */ + public int getAttributeType() { + return getUInt32AsInt(0x00); + } + + /** + * Gets the collation rule. + * @return + */ + public int getCollationRule() { + return getUInt32AsInt(0x04); + } + + /** + * Size of each index block in bytes (in the index allocation attribute). + * @return + */ + public int getIndexBlockSize() { + return getUInt32AsInt(0x08); + } + + /** + * Gets the number of clusters per index record. + * @return + */ + public int getClustersPerIndexBlock() { + final int v = getInt8(0x0C); + if (v < 0) { + return 1; + } else { + return v; + } + } +} Added: trunk/fs/src/fs/org/jnode/fs/ntfs/index/IndexRootAttribute.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/index/IndexRootAttribute.java (rev 0) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/index/IndexRootAttribute.java 2013-02-20 08:01:21 UTC (rev 5963) @@ -0,0 +1,80 @@ +/* + * $Id$ + * + * Copyright (C) 2003-2012 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.fs.ntfs.index; + +import java.util.Iterator; + +import org.jnode.fs.ntfs.FileRecord; +import org.jnode.fs.ntfs.NTFSResidentAttribute; + +/** + * @author Ewout Prangsma (ep...@us...) + */ +public final class IndexRootAttribute extends NTFSResidentAttribute { + + private IndexRoot root; + + private IndexHeader header; + + /** + * @param fileRecord + * @param offset + */ + public IndexRootAttribute(FileRecord fileRecord, int offset) { + super(fileRecord, offset); + } + + /** + * Gets the index root structure. + * + * @return + */ + public IndexRoot getRoot() { + if (root == null) { + root = new IndexRoot(this); + } + return root; + } + + /** + * Gets the index header structure. + * + * @return + */ + public IndexHeader getHeader() { + if (header == null) { + header = new IndexHeader(this); + } + return header; + } + + /**... [truncated message content] |
From: <ga...@us...> - 2013-02-20 08:18:19
|
Revision: 5964 http://jnode.svn.sourceforge.net/jnode/?rev=5964&view=rev Author: galatnm Date: 2013-02-20 08:18:08 +0000 (Wed, 20 Feb 2013) Log Message: ----------- Move attribute related classes into a sub-package. (Luke Quinane) Modified Paths: -------------- trunk/fs/src/fs/org/jnode/fs/ntfs/CompressedDataRun.java trunk/fs/src/fs/org/jnode/fs/ntfs/DataRun.java trunk/fs/src/fs/org/jnode/fs/ntfs/FileNameAttribute.java trunk/fs/src/fs/org/jnode/fs/ntfs/FileRecord.java trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSFile.java trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSFileSystem.java trunk/fs/src/fs/org/jnode/fs/ntfs/StandardInformationAttribute.java trunk/fs/src/fs/org/jnode/fs/ntfs/index/IndexAllocationAttribute.java trunk/fs/src/fs/org/jnode/fs/ntfs/index/IndexRootAttribute.java trunk/fs/src/fs/org/jnode/fs/ntfs/index/NTFSIndex.java Added Paths: ----------- trunk/fs/src/fs/org/jnode/fs/ntfs/attribute/ trunk/fs/src/fs/org/jnode/fs/ntfs/attribute/AttributeListAttribute.java trunk/fs/src/fs/org/jnode/fs/ntfs/attribute/AttributeListAttributeNonRes.java trunk/fs/src/fs/org/jnode/fs/ntfs/attribute/AttributeListAttributeRes.java trunk/fs/src/fs/org/jnode/fs/ntfs/attribute/AttributeListBlock.java trunk/fs/src/fs/org/jnode/fs/ntfs/attribute/AttributeListEntry.java trunk/fs/src/fs/org/jnode/fs/ntfs/attribute/NTFSAttribute.java trunk/fs/src/fs/org/jnode/fs/ntfs/attribute/NTFSNonResidentAttribute.java trunk/fs/src/fs/org/jnode/fs/ntfs/attribute/NTFSResidentAttribute.java Removed Paths: ------------- trunk/fs/src/fs/org/jnode/fs/ntfs/AttributeListAttribute.java trunk/fs/src/fs/org/jnode/fs/ntfs/AttributeListAttributeNonRes.java trunk/fs/src/fs/org/jnode/fs/ntfs/AttributeListAttributeRes.java trunk/fs/src/fs/org/jnode/fs/ntfs/AttributeListBlock.java trunk/fs/src/fs/org/jnode/fs/ntfs/AttributeListEntry.java trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSAttribute.java trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSNonResidentAttribute.java trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSResidentAttribute.java Deleted: trunk/fs/src/fs/org/jnode/fs/ntfs/AttributeListAttribute.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/AttributeListAttribute.java 2013-02-20 08:01:21 UTC (rev 5963) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/AttributeListAttribute.java 2013-02-20 08:18:08 UTC (rev 5964) @@ -1,42 +0,0 @@ -/* - * $Id$ - * - * Copyright (C) 2003-2013 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.fs.ntfs; - -import java.io.IOException; -import java.util.Iterator; - -/** - * Common interface for both resident and non-resident attribute list - * attributes. - * - * @author Daniel Noll (da...@no...) - */ -interface AttributeListAttribute { - - /** - * Gets an iterator over all the entries in the attribute list. - * - * @return an iterator of all attribute list entries. - * @throws IOException if there is an error reading the attribute's data. - */ - Iterator<AttributeListEntry> getAllEntries() throws IOException; - -} Deleted: trunk/fs/src/fs/org/jnode/fs/ntfs/AttributeListAttributeNonRes.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/AttributeListAttributeNonRes.java 2013-02-20 08:01:21 UTC (rev 5963) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/AttributeListAttributeNonRes.java 2013-02-20 08:18:08 UTC (rev 5964) @@ -1,60 +0,0 @@ -/* - * $Id$ - * - * Copyright (C) 2003-2013 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.fs.ntfs; - -import java.io.IOException; -import java.util.Iterator; - -/** - * $ATTRIBUTE_LIST attribute, non-resident version. - * - * XXX: Is there a sensible way we can merge this with the resident version? - * - * @author Daniel Noll (da...@no...) - */ -final class AttributeListAttributeNonRes extends NTFSNonResidentAttribute implements - AttributeListAttribute { - - /** - * @param fileRecord - * @param offset - */ - public AttributeListAttributeNonRes(FileRecord fileRecord, int offset) { - super(fileRecord, offset); - } - - /** - * Gets an iterator over all the entries in the attribute list. - * - * @return an iterator of all attribute list entries. - * @throws IOException if there is an error reading the attribute's data. - */ - public Iterator<AttributeListEntry> getAllEntries() throws IOException { - // Read the actual data from wherever it happens to be located. - // TODO: Consider handling multiple data runs separately instead - // of "glueing" them all together like this. - final int nrClusters = getNumberOfVCNs(); - final byte[] data = new byte[nrClusters * getFileRecord().getVolume().getClusterSize()]; - readVCN(getStartVCN(), data, 0, nrClusters); - AttributeListBlock listBlock = new AttributeListBlock(data, 0, getAttributeActualSize()); - return listBlock.getAllEntries(); - } -} Deleted: trunk/fs/src/fs/org/jnode/fs/ntfs/AttributeListAttributeRes.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/AttributeListAttributeRes.java 2013-02-20 08:01:21 UTC (rev 5963) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/AttributeListAttributeRes.java 2013-02-20 08:18:08 UTC (rev 5964) @@ -1,57 +0,0 @@ -/* - * $Id$ - * - * Copyright (C) 2003-2013 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.fs.ntfs; - -import java.io.IOException; -import java.util.Iterator; - -/** - * $ATTRIBUTE_LIST attribute, resident version. - * - * XXX: Is there a sensible way we can merge this with the non-resident version? - * - * @author Daniel Noll (da...@no...) - */ -final class AttributeListAttributeRes extends NTFSResidentAttribute implements - AttributeListAttribute { - - /** - * @param fileRecord - * @param offset - */ - public AttributeListAttributeRes(FileRecord fileRecord, int offset) { - super(fileRecord, offset); - } - - /** - * Gets an iterator over all the entries in the attribute list. - * - * @return an iterator of all attribute list entries. - * @throws IOException if there is an error reading the attribute's data. - */ - public Iterator<AttributeListEntry> getAllEntries() throws IOException { - final byte[] data = new byte[getAttributeLength()]; - getData(getAttributeOffset(), data, 0, data.length); - AttributeListBlock listBlock = new AttributeListBlock(data, 0, getAttributeLength()); - return listBlock.getAllEntries(); - } - -} Deleted: trunk/fs/src/fs/org/jnode/fs/ntfs/AttributeListBlock.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/AttributeListBlock.java 2013-02-20 08:01:21 UTC (rev 5963) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/AttributeListBlock.java 2013-02-20 08:18:08 UTC (rev 5964) @@ -1,116 +0,0 @@ -/* - * $Id$ - * - * Copyright (C) 2003-2013 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.fs.ntfs; - -import java.util.Iterator; -import java.util.NoSuchElementException; - -/** - * Data structure containing a list of {@link AttributeListEntry} entries. - * - * @author Daniel Noll (da...@no...) - */ -final class AttributeListBlock extends NTFSStructure { - - /** - * The length of the block. - */ - private long length; - - /** - * @param data binary data for the block. - * @param offset the offset into the binary data. - * @param length the length of the attribute list block, or 0 if unknown. - */ - public AttributeListBlock(byte[] data, int offset, long length) { - super(data, offset); - this.length = length; - } - - /** - * Gets an iterator over all the entries in the attribute list. - * - * @return an iterator of all attribute list entries. - */ - public Iterator<AttributeListEntry> getAllEntries() { - return new AttributeListEntryIterator(); - } - - /** - * Iteration of attribute list entries. - */ - private class AttributeListEntryIterator implements Iterator<AttributeListEntry> { - - /** - * The next element to return. - */ - private AttributeListEntry nextElement; - - /** - * Current offset being looked at. - */ - private int offset = 0; - - /** - * Returns {@code true} if there are more elements in the iteration. - * - * @return {@code true} if there are more elements in the iteration. - */ - public boolean hasNext() { - // Safety check in case hasNext is called twice without calling next. - if (nextElement != null) { - return true; - } - - // If the length is specified, use it to determine where the block ends. - if (offset + 4 > length) { - return false; - } - - int length = getUInt16(offset + 0x04); - nextElement = new AttributeListEntry(AttributeListBlock.this, offset); - offset += length; - return true; - } - - /** - * Gets the next entry from the iteration. - * - * @return the next entry from the iteration. - */ - public AttributeListEntry next() { - if (hasNext()) { - AttributeListEntry result = nextElement; - nextElement = null; - return result; - } else { - throw new NoSuchElementException("Iterator has no more entries"); - } - } - - /** - * @throws UnsupportedOperationException always. - */ - public void remove() { - throw new UnsupportedOperationException(); - } - } -} Deleted: trunk/fs/src/fs/org/jnode/fs/ntfs/AttributeListEntry.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/AttributeListEntry.java 2013-02-20 08:01:21 UTC (rev 5963) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/AttributeListEntry.java 2013-02-20 08:18:08 UTC (rev 5964) @@ -1,144 +0,0 @@ -/* - * $Id$ - * - * Copyright (C) 2003-2013 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.fs.ntfs; - - -/** - * @author Daniel Noll (da...@no...) - */ -final class AttributeListEntry extends NTFSStructure { - - /** - * Creates the entry. - * - * @param block the containing attribute list block. - * @param offset the offset of this attribute within the block. - */ - public AttributeListEntry(AttributeListBlock block, int offset) { - super(block, offset); - } - - /** - * Gets the type of the attribute. - * - * @return the type of the attribute. - */ - public int getType() { - return getUInt32AsInt(0x00); - } - - /** - * Gets the size of the attribute, in bytes. - * - * @return the size of the attribute, in bytes. - */ - public int getSize() { - return getUInt16(0x04); - } - - /** - * Gets the length of the name. Not so useful for callers, hence private. - * @return the name length. - */ - private int getNameLength() { - return getUInt8(0x06); - } - - /** - * Gets the offset of the name. Not so useful for callers, hence private. - * @return the name offset (from the front of the entry.) - */ - private int getNameOffset() { - return getUInt8(0x07); - } - - /** - * Gets the starting VCN of the attribute, zero if the attribute is resident. - * @return the starting VCN. - */ - public int getStartingVCN() { - return getUInt16(0x08); - } - - /** - * Gets the file reference number, which is the lowest 48 bits of the MFT - * reference. This may point to the same file record which contains the - * attribute list. - * - * @return the file reference number. - */ - public long getFileReferenceNumber() { - return getUInt48(0x10); - } - - /** - * Gets the file sequence number, which is the highest 16 bits of the MFT - * reference. - * - * @return the file sequence number. - */ - public long getFileSequenceNumber() { - return getUInt16(0x16); - } - - /** - * Gets the ID of the attribute. This ID is unique within all attributes. - * @return the attribute ID. - */ - public int getAttributeID() { - return getUInt16(0x18); - } - - /** - * Gets the name of the attribute. Some attributes don't have names, and the names - * on attributes are supposedly unique within a given attribute type. - * - * @return the name of the attribute referenced by this entry. Returns the empty string - * if the attribute has no name. - */ - public String getName() { - final int nameLength = getNameLength(); - if (nameLength == 0) { - return ""; - } else { - char[] name = new char[nameLength]; - for (int i = 0, off = getNameOffset(); i < nameLength; i++, off += 2) { - name[i] = getChar16(off); - } - return new String(name); - } - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(super.toString()); - builder.append("[type=").append(getType()); - builder.append(",name=").append(getName()); - if (getStartingVCN() == 0) { - builder.append(",resident"); - } else { - builder.append(",ref=").append(getFileReferenceNumber()); - builder.append(",vcn=").append(getStartingVCN()); - } - builder.append(",id=").append(getAttributeID()).append("]"); - return builder.toString(); - } -} Modified: trunk/fs/src/fs/org/jnode/fs/ntfs/CompressedDataRun.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/CompressedDataRun.java 2013-02-20 08:01:21 UTC (rev 5963) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/CompressedDataRun.java 2013-02-20 08:18:08 UTC (rev 5964) @@ -29,7 +29,7 @@ /** * @author Daniel Noll (da...@no...) */ -final class CompressedDataRun implements DataRunInterface { +public final class CompressedDataRun implements DataRunInterface { /** * Size of a compressed block in NTFS. This is always the same even if the cluster size is not 4k. */ Modified: trunk/fs/src/fs/org/jnode/fs/ntfs/DataRun.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/DataRun.java 2013-02-20 08:01:21 UTC (rev 5963) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/DataRun.java 2013-02-20 08:18:08 UTC (rev 5964) @@ -22,11 +22,12 @@ import java.io.IOException; import java.util.Arrays; +import org.jnode.fs.ntfs.attribute.NTFSNonResidentAttribute; /** * @author Ewout Prangsma (ep...@us...) */ -final class DataRun extends NTFSStructure implements DataRunInterface { +public final class DataRun extends NTFSStructure implements DataRunInterface { /** Type of this datarun */ private final int type; Modified: trunk/fs/src/fs/org/jnode/fs/ntfs/FileNameAttribute.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/FileNameAttribute.java 2013-02-20 08:01:21 UTC (rev 5963) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/FileNameAttribute.java 2013-02-20 08:18:08 UTC (rev 5964) @@ -20,6 +20,8 @@ package org.jnode.fs.ntfs; +import org.jnode.fs.ntfs.attribute.NTFSResidentAttribute; + /** * @author Ewout Prangsma (ep...@us...) */ Modified: trunk/fs/src/fs/org/jnode/fs/ntfs/FileRecord.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/FileRecord.java 2013-02-20 08:01:21 UTC (rev 5963) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/FileRecord.java 2013-02-20 08:18:08 UTC (rev 5964) @@ -25,6 +25,11 @@ import java.util.Iterator; import java.util.List; +import org.jnode.fs.ntfs.attribute.AttributeListAttribute; +import org.jnode.fs.ntfs.attribute.AttributeListEntry; +import org.jnode.fs.ntfs.attribute.NTFSAttribute; +import org.jnode.fs.ntfs.attribute.NTFSNonResidentAttribute; +import org.jnode.fs.ntfs.attribute.NTFSResidentAttribute; import org.jnode.util.NumberUtils; /** Deleted: trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSAttribute.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSAttribute.java 2013-02-20 08:01:21 UTC (rev 5963) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSAttribute.java 2013-02-20 08:18:08 UTC (rev 5964) @@ -1,200 +0,0 @@ -/* - * $Id$ - * - * Copyright (C) 2003-2013 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.fs.ntfs; - -import org.jnode.fs.ntfs.index.IndexAllocationAttribute; -import org.jnode.fs.ntfs.index.IndexRootAttribute; - -/** - * @author Chira - * @author Ewout Prangsma (ep...@us...) - */ -public abstract class NTFSAttribute extends NTFSStructure { - - public static final class Types { - - public static final int STANDARD_INFORMATION = 0x10; - - public static final int ATTRIBUTE_LIST = 0x20; - - public static final int FILE_NAME = 0x30; - - public static final int VOLUME_VERSION = 0x40; - - public static final int OBJECT_ID = 0x40; - - public static final int SECURITY_DESCRIPTOR = 0x50; - - public static final int VOLUME_NAME = 0x60; - - public static final int VOLUME_INFORMATION = 0x70; - - public static final int DATA = 0x80; - - public static final int INDEX_ROOT = 0x90; - - public static final int INDEX_ALLOCATION = 0xA0; - - public static final int BITMAP = 0xB0; - - public static final int SYMBOLIC_LINK = 0xC0; - - public static final int REPARSE_POINT = 0xC0; - - public static final int EA_INFORMATION = 0xD0; - - public static final int EA = 0xE0; - - public static final int PROPERTY_SET = 0xF0; - - public static final int LOGGED_UTILITY_STREAM = 0x100; - } - - private final int type; - - private final int flags; - - private final FileRecord fileRecord; - - /** - * Initialize this instance. - */ - public NTFSAttribute(FileRecord fileRecord, int offset) { - super(fileRecord, offset); - this.fileRecord = fileRecord; - this.type = getUInt32AsInt(0); - this.flags = getUInt16(0x0C); - } - - /** - * @return Returns the attributeType. - */ - public int getAttributeType() { - return type; - } - - /* - * Flag |Description ------------------- 0x0001 |Compressed 0x4000 - * |Encrypted 0x8000 |Sparse - */ - public int getFlags() { - return flags; - } - - /** - * @return Returns the nameLength. - */ - public int getNameLength() { - return getUInt8(0x09); - } - - /** - * @return Returns the nameOffset. - */ - public int getNameOffset() { - return getUInt16(0x0A); - } - - /** - * @return Returns the attributeID. - */ - public int getAttributeID() { - return getUInt16(0x0E); - } - - /** - * @return Returns the attributeName. - */ - public String getAttributeName() { - // if it is named fill the attribute name - final int nameLength = getNameLength(); - if (nameLength > 0) { - final char[] namebuf = new char[nameLength]; - final int nameOffset = getNameOffset(); - for (int i = 0; i < nameLength; i++) { - namebuf[i] = getChar16(nameOffset + (i * 2)); - } - return new String(namebuf); - } - return null; - } - - /** - * @return Returns the fileRecord. - */ - public FileRecord getFileRecord() { - return this.fileRecord; - } - - /** - * @return Returns the resident. - */ - public boolean isResident() { - return (getUInt8(0x08) == 0); - } - - /** - * Gets the length of this attribute in bytes. - * - * @return the length - */ - public int getSize() { - return getUInt32AsInt(4); - } - - /** - * Create an NTFSAttribute instance suitable for the given attribute data. - * - * @param fileRecord - * @param offset - * @return the attribute - */ - public static NTFSAttribute getAttribute(FileRecord fileRecord, int offset) { - final boolean resident = (fileRecord.getUInt8(offset + 0x08) == 0); - final int type = fileRecord.getUInt32AsInt(offset + 0x00); - - switch (type) { - case Types.STANDARD_INFORMATION: - return new StandardInformationAttribute(fileRecord, offset); - case Types.ATTRIBUTE_LIST: - if (resident) { - return new AttributeListAttributeRes(fileRecord, offset); - } else { - return new AttributeListAttributeNonRes(fileRecord, offset); - } - case Types.FILE_NAME: - return new FileNameAttribute(fileRecord, offset); - case Types.INDEX_ROOT: - return new IndexRootAttribute(fileRecord, offset); - case Types.INDEX_ALLOCATION: - return new IndexAllocationAttribute(fileRecord, offset); - } - - // check the resident flag - if (resident) { - // resident - return new NTFSResidentAttribute(fileRecord, offset); - } else { - // non resident - return new NTFSNonResidentAttribute(fileRecord, offset); - } - } -} Modified: trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSFile.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSFile.java 2013-02-20 08:01:21 UTC (rev 5963) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSFile.java 2013-02-20 08:18:08 UTC (rev 5964) @@ -26,6 +26,9 @@ import org.jnode.fs.FSFile; import org.jnode.fs.FSFileSlackSpace; import org.jnode.fs.FileSystem; +import org.jnode.fs.ntfs.attribute.NTFSAttribute; +import org.jnode.fs.ntfs.attribute.NTFSNonResidentAttribute; +import org.jnode.fs.ntfs.attribute.NTFSResidentAttribute; import org.jnode.fs.ntfs.index.IndexEntry; import org.jnode.util.ByteBufferUtils; Modified: trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSFileSystem.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSFileSystem.java 2013-02-20 08:01:21 UTC (rev 5963) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSFileSystem.java 2013-02-20 08:18:08 UTC (rev 5964) @@ -28,6 +28,8 @@ import org.jnode.fs.FSEntry; import org.jnode.fs.FSFile; import org.jnode.fs.FileSystemException; +import org.jnode.fs.ntfs.attribute.NTFSAttribute; +import org.jnode.fs.ntfs.attribute.NTFSResidentAttribute; import org.jnode.fs.spi.AbstractFileSystem; /** Deleted: trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSNonResidentAttribute.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSNonResidentAttribute.java 2013-02-20 08:01:21 UTC (rev 5963) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSNonResidentAttribute.java 2013-02-20 08:18:08 UTC (rev 5964) @@ -1,220 +0,0 @@ -/* - * $Id$ - * - * Copyright (C) 2003-2013 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.fs.ntfs; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -/** - * An NTFS file attribute that has its data stored outside the attribute. The attribute itself contains a runlist - * refering to the actual data. - * @author Chira - * @author Ewout Prangsma (ep...@us...) - * @author Daniel Noll (da...@no...) (compression support) - */ -public class NTFSNonResidentAttribute extends NTFSAttribute { - - private int numberOfVCNs = 0; - - private final List<DataRunInterface> dataRuns = new ArrayList<DataRunInterface>(); - - /** - * @param fileRecord - * @param offset - */ - public NTFSNonResidentAttribute(FileRecord fileRecord, int offset) { - super(fileRecord, offset); - /* - * process the dataruns...all non resident attributes have their data outside. can find where using data runs - */ - final int dataRunsOffset = getDataRunsOffset(); - if (dataRunsOffset > 0) { - readDataRuns(dataRunsOffset); - } - } - - /** - * @see org.jnode.fs.ntfs.NTFSAttribute#processAttributeData(byte[]) - */ - /* - * public void processAttributeData(byte[] buffer) { // TODO Auto-generated method stub } - */ - - /** - * @return Returns the startVCN. - */ - public long getStartVCN() { - return getUInt32(0x10); - } - - public long getLastVCN() { - return getUInt32(0x18); - } - - /** - * @return Returns the dataRunsOffset. - */ - public int getDataRunsOffset() { - return getUInt16(0x20); - } - - /** - * Gets the compression unit size. 2 to the power of this value is the number of clusters per compression unit. - * @return the compression unit size. - */ - public int getCompressionUnitSize() { - return getUInt16(0x22); - } - - /** - * Gets the size allocated to the attribute. May be larger than the actual size of the attribute data. - * @return the size allocated to the attribute. - */ - public long getAttributeAllocatedSize() { - return getUInt32(0x28); - } - - /** - * Gets the actual size taken up by the attribute data. - * @return the actual size taken up by the attribute data. - */ - public long getAttributeActualSize() { - return getUInt32(0x30); - } - - /** - * Read the dataruns. It is called only for non resident attributes. - */ - private void readDataRuns(int parentoffset) { - int offset = parentoffset; - - long previousLCN = 0; - final List<DataRunInterface> dataruns = getDataRuns(); - long vcn = 0; - - // If this attribute is compressed we will coalesce compressed/sparse - // data run pairs into a single data run object for convenience when reading. - boolean compressed = (getFlags() & 0x0001) != 0; - boolean expectingSparseRunNext = false; - int compUnitSize = 1 << getCompressionUnitSize(); - - while (getUInt8(offset) != 0x0) { - final DataRun dataRun = new DataRun(this, offset, vcn, previousLCN); - - if (compressed) { - if (dataRun.isSparse() && expectingSparseRunNext) { - // This is the sparse run which follows a compressed run. - // The number of runs it contains does not count towards the total - // as the compressed run reports holding all the runs for the pair. - // But we do need to move the offsets. Leaving this block open in case - // later it makes sense to put some logic in here. - } else if (dataRun.getLength() == compUnitSize) { - // Compressed/sparse pairs always add to the compression unit size. If - // the unit only compresses to 16, the system will store it uncompressed. - // So this whole unit is stored as-is, we'll leave it as a normal data run. - dataruns.add(dataRun); - this.numberOfVCNs += dataRun.getLength(); - vcn += dataRun.getLength(); - previousLCN = dataRun.getCluster(); - } else { - // TODO: Is it possible for the length to be GREATER than the unit size? - dataruns.add(new CompressedDataRun(dataRun, compUnitSize)); - if (dataRun.getLength() != compUnitSize) { - expectingSparseRunNext = true; - } - - this.numberOfVCNs += compUnitSize; - vcn += compUnitSize; - previousLCN = dataRun.getCluster(); - } - } else { - // map VCN-> datarun - dataruns.add(dataRun); - this.numberOfVCNs += dataRun.getLength(); - vcn += dataRun.getLength(); - previousLCN = dataRun.getCluster(); - } - - offset += dataRun.getSize(); - } - - // check the dataruns - final int clusterSize = getFileRecord().getVolume().getClusterSize(); - // Rounds up but won't work for 0, which shouldn't occur here. - final long allocatedVCNs = (getAttributeAllocatedSize() - 1) / clusterSize + 1; - if (this.numberOfVCNs != allocatedVCNs) { - // Probably not a problem, often multiple attributes make up one allocation. - log.debug("VCN mismatch between data runs and allocated size, possibly a composite attribute. " - + "data run VCNs = " + this.numberOfVCNs + ", allocated size = " + allocatedVCNs - + ", data run count = " + dataRuns.size()); - } - } - - /** - * @return Returns the data runs. - */ - public List<DataRunInterface> getDataRuns() { - return dataRuns; - } - - /** - * Read a number of clusters starting from a given virtual cluster number (vcn). - * @param vcn - * @param nrClusters - * @return The number of clusters read. - * @throws IOException - */ - public int readVCN(long vcn, byte[] dst, int dstOffset, int nrClusters) throws IOException { - final int flags = getFlags(); - if ((flags & 0x4000) != 0) { - throw new IOException("Reading encrypted files is not supported"); - } - - if (log.isDebugEnabled()) { - log.debug("readVCN: wants start " + vcn + " length " + nrClusters + ", we have start " + getStartVCN() - + " length " + getNumberOfVCNs()); - } - - final NTFSVolume volume = getFileRecord().getVolume(); - final int clusterSize = volume.getClusterSize(); - int readClusters = 0; - for(DataRunInterface dataRun : this.getDataRuns()) { - readClusters += dataRun.readClusters(vcn, dst, dstOffset, nrClusters, clusterSize, volume); - if (readClusters == nrClusters) { - break; - } - } - - if (log.isDebugEnabled()) { - log.debug("readVCN: read " + readClusters); - } - - return readClusters; - } - - /** - * @return Returns the numberOfVNCs. - */ - public int getNumberOfVCNs() { - return numberOfVCNs; - } -} Deleted: trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSResidentAttribute.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSResidentAttribute.java 2013-02-20 08:01:21 UTC (rev 5963) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/NTFSResidentAttribute.java 2013-02-20 08:18:08 UTC (rev 5964) @@ -1,58 +0,0 @@ -/* - * $Id$ - * - * Copyright (C) 2003-2013 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.fs.ntfs; - -/** - * An NTFS file attribute that has its data stored inside the attribute. - * - * @author Chira - * @author Ewout Prangsma (ep...@us...) - */ -public class NTFSResidentAttribute extends NTFSAttribute { - - /** - * @param fileRecord - * @param offset - */ - public NTFSResidentAttribute(FileRecord fileRecord, int offset) { - super(fileRecord, offset); - } - - /** - * Gets the offset to the actual attribute. - * - * @return Returns the attributeOffset. - */ - public int getAttributeOffset() { - return getUInt16(0x14); - } - - /** - * @return Returns the indexedFlag. - */ - public int getIndexedFlag() { - return getUInt8(0x16); - } - - public int getAttributeLength() { - return (int) getUInt32(0x10); - } -} Modified: trunk/fs/src/fs/org/jnode/fs/ntfs/StandardInformationAttribute.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/StandardInformationAttribute.java 2013-02-20 08:01:21 UTC (rev 5963) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/StandardInformationAttribute.java 2013-02-20 08:18:08 UTC (rev 5964) @@ -20,6 +20,8 @@ package org.jnode.fs.ntfs; +import org.jnode.fs.ntfs.attribute.NTFSResidentAttribute; + /** * @author Daniel Noll (da...@no...) */ Added: trunk/fs/src/fs/org/jnode/fs/ntfs/attribute/AttributeListAttribute.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/attribute/AttributeListAttribute.java (rev 0) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/attribute/AttributeListAttribute.java 2013-02-20 08:18:08 UTC (rev 5964) @@ -0,0 +1,42 @@ +/* + * $Id: header.txt 5714 2010-01-03 13:33:07Z lsantha $ + * + * Copyright (C) 2003-2012 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.fs.ntfs.attribute; + +import java.io.IOException; +import java.util.Iterator; + +/** + * Common interface for both resident and non-resident attribute list + * attributes. + * + * @author Daniel Noll (da...@no...) + */ +public interface AttributeListAttribute { + + /** + * Gets an iterator over all the entries in the attribute list. + * + * @return an iterator of all attribute list entries. + * @throws IOException if there is an error reading the attribute's data. + */ + Iterator<AttributeListEntry> getAllEntries() throws IOException; + +} Added: trunk/fs/src/fs/org/jnode/fs/ntfs/attribute/AttributeListAttributeNonRes.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/attribute/AttributeListAttributeNonRes.java (rev 0) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/attribute/AttributeListAttributeNonRes.java 2013-02-20 08:18:08 UTC (rev 5964) @@ -0,0 +1,61 @@ +/* + * $Id: header.txt 5714 2010-01-03 13:33:07Z lsantha $ + * + * Copyright (C) 2003-2012 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.fs.ntfs.attribute; + +import java.io.IOException; +import java.util.Iterator; +import org.jnode.fs.ntfs.FileRecord; + +/** + * $ATTRIBUTE_LIST attribute, non-resident version. + * + * XXX: Is there a sensible way we can merge this with the resident version? + * + * @author Daniel Noll (da...@no...) + */ +final class AttributeListAttributeNonRes extends NTFSNonResidentAttribute implements + AttributeListAttribute { + + /** + * @param fileRecord + * @param offset + */ + public AttributeListAttributeNonRes(FileRecord fileRecord, int offset) { + super(fileRecord, offset); + } + + /** + * Gets an iterator over all the entries in the attribute list. + * + * @return an iterator of all attribute list entries. + * @throws IOException if there is an error reading the attribute's data. + */ + public Iterator<AttributeListEntry> getAllEntries() throws IOException { + // Read the actual data from wherever it happens to be located. + // TODO: Consider handling multiple data runs separately instead + // of "glueing" them all together like this. + final int nrClusters = getNumberOfVCNs(); + final byte[] data = new byte[nrClusters * getFileRecord().getVolume().getClusterSize()]; + readVCN(getStartVCN(), data, 0, nrClusters); + AttributeListBlock listBlock = new AttributeListBlock(data, 0, getAttributeActualSize()); + return listBlock.getAllEntries(); + } +} Added: trunk/fs/src/fs/org/jnode/fs/ntfs/attribute/AttributeListAttributeRes.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/attribute/AttributeListAttributeRes.java (rev 0) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/attribute/AttributeListAttributeRes.java 2013-02-20 08:18:08 UTC (rev 5964) @@ -0,0 +1,58 @@ +/* + * $Id: header.txt 5714 2010-01-03 13:33:07Z lsantha $ + * + * Copyright (C) 2003-2012 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.fs.ntfs.attribute; + +import java.io.IOException; +import java.util.Iterator; +import org.jnode.fs.ntfs.FileRecord; + +/** + * $ATTRIBUTE_LIST attribute, resident version. + * + * XXX: Is there a sensible way we can merge this with the non-resident version? + * + * @author Daniel Noll (da...@no...) + */ +final class AttributeListAttributeRes extends NTFSResidentAttribute implements + AttributeListAttribute { + + /** + * @param fileRecord + * @param offset + */ + public AttributeListAttributeRes(FileRecord fileRecord, int offset) { + super(fileRecord, offset); + } + + /** + * Gets an iterator over all the entries in the attribute list. + * + * @return an iterator of all attribute list entries. + * @throws IOException if there is an error reading the attribute's data. + */ + public Iterator<AttributeListEntry> getAllEntries() throws IOException { + final byte[] data = new byte[getAttributeLength()]; + getData(getAttributeOffset(), data, 0, data.length); + AttributeListBlock listBlock = new AttributeListBlock(data, 0, getAttributeLength()); + return listBlock.getAllEntries(); + } + +} Added: trunk/fs/src/fs/org/jnode/fs/ntfs/attribute/AttributeListBlock.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/attribute/AttributeListBlock.java (rev 0) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/attribute/AttributeListBlock.java 2013-02-20 08:18:08 UTC (rev 5964) @@ -0,0 +1,117 @@ +/* + * $Id: header.txt 5714 2010-01-03 13:33:07Z lsantha $ + * + * Copyright (C) 2003-2012 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.fs.ntfs.attribute; + +import java.util.Iterator; +import java.util.NoSuchElementException; +import org.jnode.fs.ntfs.NTFSStructure; + +/** + * Data structure containing a list of {@link AttributeListEntry} entries. + * + * @author Daniel Noll (da...@no...) + */ +final class AttributeListBlock extends NTFSStructure { + + /** + * The length of the block. + */ + private long length; + + /** + * @param data binary data for the block. + * @param offset the offset into the binary data. + * @param length the length of the attribute list block, or 0 if unknown. + */ + public AttributeListBlock(byte[] data, int offset, long length) { + super(data, offset); + this.length = length; + } + + /** + * Gets an iterator over all the entries in the attribute list. + * + * @return an iterator of all attribute list entries. + */ + public Iterator<AttributeListEntry> getAllEntries() { + return new AttributeListEntryIterator(); + } + + /** + * Iteration of attribute list entries. + */ + private class AttributeListEntryIterator implements Iterator<AttributeListEntry> { + + /** + * The next element to return. + */ + private AttributeListEntry nextElement; + + /** + * Current offset being looked at. + */ + private int offset = 0; + + /** + * Returns {@code true} if there are more elements in the iteration. + * + * @return {@code true} if there are more elements in the iteration. + */ + public boolean hasNext() { + // Safety check in case hasNext is called twice without calling next. + if (nextElement != null) { + return true; + } + + // If the length is specified, use it to determine where the block ends. + if (offset + 4 > length) { + return false; + } + + int length = getUInt16(offset + 0x04); + nextElement = new AttributeListEntry(AttributeListBlock.this, offset); + offset += length; + return true; + } + + /** + * Gets the next entry from the iteration. + * + * @return the next entry from the iteration. + */ + public AttributeListEntry next() { + if (hasNext()) { + AttributeListEntry result = nextElement; + nextElement = null; + return result; + } else { + throw new NoSuchElementException("Iterator has no more entries"); + } + } + + /** + * @throws UnsupportedOperationException always. + */ + public void remove() { + throw new UnsupportedOperationException(); + } + } +} Added: trunk/fs/src/fs/org/jnode/fs/ntfs/attribute/AttributeListEntry.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/attribute/AttributeListEntry.java (rev 0) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/attribute/AttributeListEntry.java 2013-02-20 08:18:08 UTC (rev 5964) @@ -0,0 +1,146 @@ +/* + * $Id: header.txt 5714 2010-01-03 13:33:07Z lsantha $ + * + * Copyright (C) 2003-2012 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.fs.ntfs.attribute; + + +import org.jnode.fs.ntfs.NTFSStructure; + +/** + * @author Daniel Noll (da...@no...) + */ +public final class AttributeListEntry extends NTFSStructure { + + /** + * Creates the entry. + * + * @param block the containing attribute list block. + * @param offset the offset of this attribute within the block. + */ + public AttributeListEntry(AttributeListBlock block, int offset) { + super(block, offset); + } + + /** + * Gets the type of the attribute. + * + * @return the type of the attribute. + */ + public int getType() { + return getUInt32AsInt(0x00); + } + + /** + * Gets the size of the attribute, in bytes. + * + * @return the size of the attribute, in bytes. + */ + public int getSize() { + return getUInt16(0x04); + } + + /** + * Gets the length of the name. Not so useful for callers, hence private. + * @return the name length. + */ + private int getNameLength() { + return getUInt8(0x06); + } + + /** + * Gets the offset of the name. Not so useful for callers, hence private. + * @return the name offset (from the front of the entry.) + */ + private int getNameOffset() { + return getUInt8(0x07); + } + + /** + * Gets the starting VCN of the attribute, zero if the attribute is resident. + * @return the starting VCN. + */ + public int getStartingVCN() { + return getUInt16(0x08); + } + + /** + * Gets the file reference number, which is the lowest 48 bits of the MFT + * reference. This may point to the same file record which contains the + * attribute list. + * + * @return the file reference number. + */ + public long getFileReferenceNumber() { + return getUInt48(0x10); + } + + /** + * Gets the file sequence number, which is the highest 16 bits of the MFT + * reference. + * + * @return the file sequence number. + */ + public long getFileSequenceNumber() { + return getUInt16(0x16); + } + + /** + * Gets the ID of the attribute. This ID is unique within all attributes. + * @return the attribute ID. + */ + public int getAttributeID() { + return getUInt16(0x18); + } + + /** + * Gets the name of the attribute. Some attributes don't have names, and the names + * on attributes are supposedly unique within a given attribute type. + * + * @return the name of the attribute referenced by this entry. Returns the empty string + * if the attribute has no name. + */ + public String getName() { + final int nameLength = getNameLength(); + if (nameLength == 0) { + return ""; + } else { + char[] name = new char[nameLength]; + for (int i = 0, off = getNameOffset(); i < nameLength; i++, off += 2) { + name[i] = getChar16(off); + } + return new String(name); + } + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(super.toString()); + builder.append("[type=").append(getType()); + builder.append(",name=").append(getName()); + if (getStartingVCN() == 0) { + builder.append(",resident"); + } else { + builder.append(",ref=").append(getFileReferenceNumber()); + builder.append(",vcn=").append(getStartingVCN()); + } + builder.append(",id=").append(getAttributeID()).append("]"); + return builder.toString(); + } +} Added: trunk/fs/src/fs/org/jnode/fs/ntfs/attribute/NTFSAttribute.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/attribute/NTFSAttribute.java (rev 0) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/attribute/NTFSAttribute.java 2013-02-20 08:18:08 UTC (rev 5964) @@ -0,0 +1,204 @@ +/* + * $Id$ + * + * Copyright (C) 2003-2012 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.fs.ntfs.attribute; + +import org.jnode.fs.ntfs.FileNameAttribute; +import org.jnode.fs.ntfs.FileRecord; +import org.jnode.fs.ntfs.NTFSStructure; +import org.jnode.fs.ntfs.StandardInformationAttribute; +import org.jnode.fs.ntfs.index.IndexAllocationAttribute; +import org.jnode.fs.ntfs.index.IndexRootAttribute; + +/** + * @author Chira + * @author Ewout Prangsma (ep...@us...) + */ +public abstract class NTFSAttribute extends NTFSStructure { + + public static final class Types { + + public static final int STANDARD_INFORMATION = 0x10; + + public static final int ATTRIBUTE_LIST = 0x20; + + public static final int FILE_NAME = 0x30; + + public static final int VOLUME_VERSION = 0x40; + + public static final int OBJECT_ID = 0x40; + + public static final int SECURITY_DESCRIPTOR = 0x50; + + public static final int VOLUME_NAME = 0x60; + + public static final int VOLUME_INFORMATION = 0x70; + + public static final int DATA = 0x80; + + public static final int INDEX_ROOT = 0x90; + + public static final int INDEX_ALLOCATION = 0xA0; + + public static final int BITMAP = 0xB0; + + public static final int SYMBOLIC_LINK = 0xC0; + + public static final int REPARSE_POINT = 0xC0; + + public static final int EA_INFORMATION = 0xD0; + + public static final int EA = 0xE0; + + public static final int PROPERTY_SET = 0xF0; + + public static final int LOGGED_UTILITY_STREAM = 0x100; + } + + private final int type; + + private final int flags; + + private final FileRecord fileRecord; + + /** + * Initialize this instance. + */ + public NTFSAttribute(FileRecord fileRecord, int offset) { + super(fileRecord, offset); + this.fileRecord = fileRecord; + this.type = getUInt32AsInt(0); + this.flags = getUInt16(0x0C); + } + + /** + * @return Returns the attributeType. + */ + public int getAttributeType() { + return type; + } + + /* + * Flag |Description ------------------- 0x0001 |Compressed 0x4000 + * |Encrypted 0x8000 |Sparse + */ + public int getFlags() { + return flags; + } + + /** + * @return Returns the nameLength. + */ + public int getNameLength() { + return getUInt8(0x09); + } + + /** + * @return Returns the nameOffset. + */ + public int getNameOffset() { + return getUInt16(0x0A); + } + + /** + * @return Returns the attributeID. + */ + public int getAttributeID() { + return getUInt16(0x0E); + } + + /** + * @return Returns the attributeName. + */ + public String getAttributeName() { + // if it is named fill the attribute name + final int nameLength = getNameLength(); + if (nameLength > 0) { + final char[] namebuf = new char[nameLength]; + final int nameOffset = getNameOffset(); + for (int i = 0; i < nameLength; i++) { + namebuf[i] = getChar16(nameOffset + (i * 2)); + } + return new String(namebuf); + } + return null; + } + + /** + * @return Returns the fileRecord. + */ + public FileRecord getFileRecord() { + return this.fileRecord; + } + + /** + * @return Returns the resident. + */ + public boolean isResident() { + return (getUInt8(0x08) == 0); + } + + /** + * Gets the length of this attribute in bytes. + * + * @return the length + */ + public int getSize() { + return getUInt32AsInt(4); + } + + /** + * Create an NTFSAttribute instance suitable for the given attribute data. + * + * @param fileRecord + * @param offset + * @return the attribute + */ + public static NTFSAttribute getAttribute(FileRecord fileRecord, int offset) { + final boolean resident = (fileRecord.getUInt8(offset + 0x08) == 0); + final int type = fileRecord.getUInt32AsInt(offset + 0x00); + + switch (type) { + case Types.STANDARD_INFORMATION: + return new StandardInformationAttribute(fileRecord, offset); + case Types.ATTRIBUTE_LIST: + if (resident) { + return new AttributeListAttributeRes(fileRecord, offset); + } else { + return new AttributeListAttributeNonRes(fileRecord, offset); + } + case Types.FILE_NAME: + return new FileNameAttribute(fileRecord, offset); + case Types.INDEX_ROOT: + return new IndexRootAttribute(fileRecord, offset); + case Types.INDEX_ALLOCATION: + return new IndexAllocationAttribute(fileRecord, offset); + } + + // check the resident flag + if (resident) { + // resident + return new NTFSResidentAttribute(fileRecord, offset); + } else { + // non resident + return new NTFSNonResidentAttribute(fileRecord, offset); + } + } +} Added: trunk/fs/src/fs/org/jnode/fs/ntfs/attribute/NTFSNonResidentAttribute.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/ntfs/attribute/NTFSNonResidentAttribute.java (rev 0) +++ trunk/fs/src/fs/org/jnode/fs/ntfs/attribute/NTFSNonResidentAttribute.java 2013-02-20 08:18:08 UTC (rev 5964) @@ -0,0 +1,235 @@ +/* + * $Id$ + * + * Copyright (C) 2003-2012 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 ... [truncated message content] |