package org.jnode.fs.ext2.xattr; import java.util.HashMap; import java.util.Map; import org.apache.log4j.Logger; import org.jnode.fs.ext2.Ext2FileSystem; import org.jnode.util.LittleEndian; /** * An extended attribute entry. * * @author Luke Quinane */ public class XAttrEntry { /** * The logger. */ private final Logger log = Logger.getLogger(getClass()); /** * The minimum size of the entry structure. */ public static final int MINIMUM_SIZE = 16; /** * The map of name index to well-known name prefixes. */ private static final Map<Integer, String> NAME_INDEX_PREFIX_MAP = new HashMap<Integer, String>(); static { NAME_INDEX_PREFIX_MAP.put(1, "user."); NAME_INDEX_PREFIX_MAP.put(2, "system.posix_acl_access"); NAME_INDEX_PREFIX_MAP.put(3, "system.posix_acl_default"); NAME_INDEX_PREFIX_MAP.put(4, "trusted."); NAME_INDEX_PREFIX_MAP.put(5, "security."); NAME_INDEX_PREFIX_MAP.put(7, "system."); NAME_INDEX_PREFIX_MAP.put(8, "system.richacl"); } /** * The data for the attribute entry. */ private final byte[] data; /** * The offset into the data. */ private final int offset; /** * Creates a new entry with the given data. * * @param data the data. * @param offset the offset into the entry. */ public XAttrEntry(byte[] data, int offset) { this.data = data; this.offset = offset; } /** * Gets the name length. * * @return the name length. */ public int getNameLength() { return LittleEndian.getUInt8(data, offset + 0); } /** * Gets the name prefix index, 0 means no prefix. * * @return the name prefix index. */ public int getNameIndex() { return LittleEndian.getUInt8(data, offset + 1); } /** * Gets the offset to the value on the disk block where it is stored. For an inode attribute this value is relative * to the start of the first entry; for a block this value is relative to the start of the block (i.e. the header). * * @return the value offset. */ public int getValueOffset() { return LittleEndian.getUInt16(data, offset + 2); } /** * Gets the disk block where the value is stored. Zero indicates the value is in the same block as this entry. * * @return the value block. */ public long getValueBlock() { return LittleEndian.getUInt32(data, offset + 4); } /** * Gets the value size. * * @return the value size. */ public long getValueSize() { return LittleEndian.getUInt32(data, offset + 8); } /** * Gets the hash. * * @return the hash. */ public long getHash() { return LittleEndian.getUInt32(data, offset + 0xc); } /** * Gets the attribute name. * * @return the attribute name. */ public String getName() { String name = new String(data, offset + 0x10, getNameLength(), Ext2FileSystem.ENTRY_NAME_CHARSET); int prefixIndex = getNameIndex(); if (prefixIndex != 0) { String prefix = NAME_INDEX_PREFIX_MAP.get(prefixIndex); if (prefix != null) { name = prefix + name; } else { log.warn("Unknown xattr prefix index: " + prefixIndex); } } return name; } /** * Gets the attribute value. * * @return the value. */ public byte[] getValue() { long valueBlockNumber = getValueBlock(); if (valueBlockNumber != 0) { throw new UnsupportedOperationException("Value is stored in another block and that is not implemented yet"); } byte[] value = new byte[(int) getValueSize()]; System.arraycopy(data, getValueOffset(), value, 0, value.length); return value; } }