/* * $Id$ * * Copyright (C) 2003-2015 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.jfat; import java.io.IOException; import org.jnode.util.NumberUtils; /** * @author gvt */ public class FatLongDirEntry extends FatDirEntry { public static final int NAMELENGTH = 13; public static final int UNAMELENGTH = NAMELENGTH * 2; private static final int LAST_LONG_ENTRY = 0x40; private static final int LONG_ENTRY_MASK = 0x3F; private static final int LONG_ENTRY_MIN = 0x01; private static final int LONG_ENTRY_MAX = 0x7F; private static final String charsetName = "UTF-16LE"; /* * encoded side */ private int lOrd; private byte[] lName; private int lAttr; private int lType; private int lChksum; private int lFstClusLo; /* * decoded side */ private byte ordinal; private boolean last; private boolean damaged; private String component; private FatAttr attr; private byte chksum; public FatLongDirEntry(FatFileSystem fs, FatMarshal entry, int index) throws IOException { super(fs, entry, index); decode(); } public FatLongDirEntry(FatFileSystem fs, String component, byte ordinal, byte chksum, boolean last, int index) throws IOException { this(fs, new FatMarshal(LENGTH), index); setOrdinal(ordinal); setLast(last); setComponent(component); setAttr(); setChkSum(chksum); setDamaged(false); } private void decodeOrdinal() { lOrd = entry.getUInt8(0); if (lOrd < LONG_ENTRY_MIN || lOrd > LONG_ENTRY_MAX) { damaged = true; ordinal = 1; last = false; return; } ordinal = (byte) (lOrd & LONG_ENTRY_MASK); last = ((lOrd & LAST_LONG_ENTRY) != 0); damaged = false; } private void encodeOrdinal() { if (ordinal < LONG_ENTRY_MIN || ordinal > LONG_ENTRY_MAX) throw new IllegalArgumentException("ordinal is invalid: " + NumberUtils.hex(ordinal, 2)); lOrd = ordinal; if (last) lOrd |= LAST_LONG_ENTRY; entry.setUInt8(0, lOrd); } private void decodeComponent() throws IOException { lName = new byte[UNAMELENGTH]; entry.getBytes(1, 10, 0, lName); entry.getBytes(14, 12, 10, lName); entry.getBytes(28, 4, 22, lName); int i, l; int m = lName.length / 2; /* * if the name is Unicode NUL (0x0000) terminated "l" will end with the * Unicode string length ( < 13 ) otherwise the length will be exactly * 13 and will not be terminated or padded at all */ for (l = 0; l < m; l++) if ((lName[2 * l] == (byte) 0x00) && (lName[2 * l + 1] == (byte) 0x00)) break; /* * check to see if the name is correctly 0xFFFF padded (page 28) - note * that the name is not padded if it exactly fits the 13 Unicode char * position and it is only NUL terminated if has a length of 12 */ for (i = l + 1; i < m; i++) { if ((lName[2 * i] != (byte) 0xFF) || (lName[2 * i + 1] != (byte) 0xFF)) { damaged = true; component = ""; return; } } /* * if the entry is not padded correctly is marked "damaged" and will * considered an orphan entry the thread will not reach this point ... * game is over ... */ component = new String(lName, 0, 2 * l, charsetName); } private void encodeComponent() throws IOException { lName = new byte[UNAMELENGTH]; int l; int m = lName.length / 2; byte[] componentArray = component.getBytes(charsetName); l = componentArray.length; if (l > lName.length) throw new IllegalArgumentException("component length exceed limit: " + l); if (l == 0) throw new IllegalArgumentException("component has zero length"); if ((l % 2) == 1) throw new IllegalArgumentException("component has an odd byte length: " + l); System.arraycopy(componentArray, 0, lName, 0, l); l = l / 2; /* * NUL terminate the component and PAD with 0xFFFF if needed */ if (l < m) { lName[2 * l] = (byte) 0x00; lName[2 * l + 1] = (byte) 0x00; for (int i = l + 1; i < m; i++) { lName[2 * i] = (byte) 0xFF; lName[2 * i + 1] = (byte) 0xFF; } } entry.setBytes(1, 10, 0, lName); entry.setBytes(14, 12, 10, lName); entry.setBytes(28, 4, 22, lName); } private void decodeAttr() { lAttr = entry.getUInt8(11); attr = new FatAttr(lAttr); if (!attr.isLong()) damaged = true; } private void encodeAttr() { if (!attr.isLong()) throw new IllegalArgumentException("attribute is not LONG"); lAttr = attr.getAttr(); entry.setUInt8(11, lAttr); } private void decodeChksum() { lChksum = entry.getUInt8(13); chksum = (byte) lChksum; } private void encodeChksum() { lChksum = chksum; entry.setUInt8(13, lChksum); } private void decodeOthers() { lType = entry.getUInt8(12); if (lType != 0) damaged = true; lFstClusLo = entry.getUInt16(26); if (lFstClusLo != 0) damaged = true; } private void encodeOthers() { lType = 0; entry.setUInt8(12, lType); lFstClusLo = 0; entry.setUInt16(26, lFstClusLo); } private void decode() throws IOException { decodeOrdinal(); decodeComponent(); decodeAttr(); decodeChksum(); decodeOthers(); } @SuppressWarnings("unused") private void encode() throws IOException { encodeOrdinal(); encodeComponent(); encodeAttr(); encodeChksum(); encodeOthers(); } public boolean isLongDirEntry() { return true; } public byte getOrdinal() { return ordinal; } public void setOrdinal(byte value) { ordinal = value; encodeOrdinal(); } public boolean isLast() { return last; } public void setLast(boolean value) { last = value; encodeOrdinal(); } public String getComponent() { return component; } public void setComponent(String value) throws IOException { component = value; encodeComponent(); } public FatAttr getAttr() { return attr; } public void setAttr() { FatAttr longattr = new FatAttr(); longattr.setLong(); attr = longattr; encodeAttr(); } public byte getChkSum() { return chksum; } public void setChkSum(byte value) { chksum = value; encodeChksum(); } public boolean isDamaged() { return damaged; } public void setDamaged(boolean value) { damaged = value; } @Override public String toString() { return String.format( "Long Entry [%s] index:%d attr:%s type:%d ckhsum:%s last:%b", component, getIndex(), NumberUtils.hex(lAttr, 2), lType, NumberUtils.hex(lChksum, 2), isLast()); } public String toDebugString() { StrWriter out = new StrWriter(); out.println("*******************************************"); out.println("Long Entry\tisDirty[" + isDirty() + "]"); out.println("*******************************************"); out.println("Index\t\t" + getIndex()); out.println("Entry"); out.println(entry.getArray()); out.println("Ord\t\t" + NumberUtils.hex(lOrd, 2)); out.println("Name"); out.println(lName); out.println("Attr\t\t" + NumberUtils.hex(lAttr, 2)); out.println("Type\t\t" + lType); out.println("Chksum\t\t" + NumberUtils.hex(lChksum, 2)); out.println("FstClusLo\t" + lFstClusLo); out.println("*******************************************"); out.println("Ordinal\t\t" + getOrdinal()); out.println("isLast\t\t" + isLast()); out.println("isDamaged\t" + isDamaged()); out.println("Component\t" + "<" + getComponent() + ">"); out.println("ChkSum\t\t" + NumberUtils.hex(getChkSum(), 2)); out.println("Attr\t\t" + getAttr()); out.print("*******************************************"); return out.toString(); } }