/* * $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 java.nio.charset.CharacterCodingException; import java.util.Arrays; import org.apache.log4j.Logger; import org.jnode.util.NumberUtils; /** * @author gvt */ public class FatShortDirEntry extends FatDirEntry { private static final Logger log = Logger.getLogger(FatShortDirEntry.class); /* * encoded side */ protected byte[] lName; private int lAttr; private int lNTRes; private int lCrtTimeTenth; private int lCrtTime; private int lCrtDate; private int lLstAccDate; private int lFstClusHi; private int lWrtTime; private int lWrtDate; private int lFstClusLo; private long lFileSize; /* * decoded side */ private FatCase ncase; private FatAttr attr; private String base; private String ext; private long created; private long accessed; private long modified; private int cluster; private long length; protected FatShortDirEntry(FatFileSystem fs) { super(fs, new FatMarshal(LENGTH), 0); } public FatShortDirEntry(FatFileSystem fs, FatMarshal entry, int index) { super(fs, entry, index); decode(); } public FatShortDirEntry(FatFileSystem fs, FatName name, int index) throws IOException { this(fs, new FatMarshal(LENGTH), index); long now = System.currentTimeMillis(); setNameCase(name.getShortCase()); setAttr(new FatAttr()); setName(name.getName()); setCreated(now); setLastAccessed(now); setLastModified(now); setStartCluster(0); setLength(0); } protected void decodeName() { lName = entry.getBytes(0, 11); /* * handle the special character 0x05 (page 23) 0xE5 is a valid KANJI * (japanese) character it cannot stay on persistent storage (as it was * choosed for FREE entries) so it will changed from 0x05 to 0xE5 in * memory */ if (lName[0] == (byte) KANJI) lName[0] = (byte) FREE; decodeBase(); decodeExt(); } protected void encodeName() { /* * handle the special character 0x05 (page 23) 0xE5 is a valid KANJI * (japanese) character it cannot stay on persistent storage (as it was * choosed for FREE entries) so it will changed from 0x05 to 0xE5 in * memory */ if (lName[0] == (byte) FREE) lName[0] = (byte) KANJI; decodeBase(); decodeExt(); entry.setBytes(0, 11, lName); } protected void decodeBase() { String baseName; byte[] basebuf = new byte[8]; System.arraycopy(lName, 0, basebuf, 0, 8); try { baseName = getFatFileSystem().getCodePage().newDecoder().decode(basebuf); } catch (CharacterCodingException ex) { log.debug("CharacterCodingException: CodePage error"); log.debug("go on with standard decoding"); baseName = base; } if (ncase.isLowerBase()) base = baseName.trim().toLowerCase(); else base = baseName.trim().toUpperCase(); } protected void decodeExt() { String extName; byte[] extbuf = new byte[3]; System.arraycopy(lName, 8, extbuf, 0, 3); try { extName = getFatFileSystem().getCodePage().newDecoder().decode(extbuf); } catch (CharacterCodingException ex) { log.debug("CharacterCodingException: CodePage error"); log.debug("go on with standard decoding"); extName = ext; } if (ncase.isLowerExt()) ext = extName.trim().toLowerCase(); else ext = extName.trim().toUpperCase(); } protected void decodeAttr() { lAttr = entry.getUInt8(11); attr = new FatAttr(lAttr); } private void encodeAttr() { lAttr = attr.getAttr(); entry.setUInt8(11, lAttr); } protected void decodeNameCase() { lNTRes = entry.getUInt8(12); ncase = new FatCase(lNTRes); } private void encodeNameCase() { lNTRes = ncase.getCase(); entry.setUInt8(12, lNTRes); } protected void decodeCreated() { lCrtTimeTenth = entry.getUInt8(13); lCrtTime = entry.getUInt16(14); lCrtDate = entry.getUInt16(16); created = FatUtils.decodeDateTime(lCrtDate, lCrtTime, lCrtTimeTenth); } private void encodeCreated() { lCrtDate = FatUtils.encodeDate(created); lCrtTime = FatUtils.encodeTime(created); /* * GVT???: this have to be tested against a real M$ OS how the Tenth is * actually handled at entry creation? for now just avoid to store the * tenth as Mtools seems to do */ lCrtTimeTenth = 0; // FatUtils.encodeTenth ( created ); entry.setUInt8(13, lCrtTimeTenth); entry.setUInt16(14, lCrtTime); entry.setUInt16(16, lCrtDate); } protected void decodeAccessed() { lLstAccDate = entry.getUInt16(18); accessed = FatUtils.decodeDateTime(lLstAccDate, 0); } private void encodeAccessed() { lLstAccDate = FatUtils.encodeDate(accessed); entry.setUInt16(18, lLstAccDate); } protected void decodeModified() { lWrtTime = entry.getUInt16(22); lWrtDate = entry.getUInt16(24); modified = FatUtils.decodeDateTime(lWrtDate, lWrtTime); } private void encodeModified() { lWrtDate = FatUtils.encodeDate(modified); lWrtTime = FatUtils.encodeTime(modified); entry.setUInt16(22, lWrtTime); entry.setUInt16(24, lWrtDate); } protected void decodeCluster() { lFstClusHi = entry.getUInt16(20); lFstClusLo = entry.getUInt16(26); /* * be sure startCluster is not larger than 28 bits FAT32 is actually a * FAT28 ;-) should't happen at all ... but who knows? */ if (lFstClusLo > 0xFFFF) throw new IllegalArgumentException("FstClusLo too large: " + NumberUtils.hex(lFstClusLo, 4)); if (lFstClusHi > 0xFFF) throw new IllegalArgumentException("FstClusHi too large: " + NumberUtils.hex(lFstClusHi, 4)); /* * FstClusHi have to be "zero" for FAT12/FAT16 remind to add a check * here */ cluster = (lFstClusHi << 16) + lFstClusLo; } private void encodeCluster() { /* * be sure startCluster is not larger than 28 bits FAT32 is actually a * FAT28 ;-) should't happen at all ... but who knows? */ if (cluster < 0 || cluster > 0x0FFFFFFF) throw new IllegalArgumentException("cluster is invalid: " + NumberUtils.hex(cluster, 8)); lFstClusLo = cluster & 0x0000FFFF; lFstClusHi = (cluster >> 16) & 0x00000FFF; entry.setUInt16(20, lFstClusHi); entry.setUInt16(26, lFstClusLo); } protected void decodeLength() { lFileSize = entry.getUInt32(28); length = lFileSize; } private void encodeLength() { if (length < 0L || length > 0xFFFFFFFFL) throw new IllegalArgumentException("length is invalid: " + length); lFileSize = length; entry.setUInt32(28, lFileSize); } protected void decode() { decodeNameCase(); decodeAttr(); decodeName(); decodeCreated(); decodeAccessed(); decodeModified(); decodeCluster(); decodeLength(); } @SuppressWarnings("unused") private void encode() { encodeNameCase(); encodeAttr(); encodeName(); encodeCreated(); encodeAccessed(); encodeModified(); encodeCluster(); encodeLength(); } public boolean isShortDirEntry() { return true; } private FatCase getNameCase() { return ncase; } public boolean isBaseLowerCase() { return ncase.isLowerBase(); } public boolean isExtLowertCase() { return ncase.isLowerExt(); } public void setNameCase(FatCase value) { ncase = value; encodeNameCase(); } protected FatAttr getAttr() { return attr; } protected void setAttr(FatAttr value) { attr = value; encodeAttr(); } public boolean isReadOnly() { return attr.isReadOnly(); } public void setReadOnly() { attr.setReadOnly(true); encodeAttr(); } public boolean isHidden() { return attr.isHidden(); } public void setHidden() { attr.setHidden(true); encodeAttr(); } public boolean isSystem() { return attr.isSystem(); } public void setSystem() { attr.setSystem(true); encodeAttr(); } public boolean isLabel() { return attr.isLabel(); } public void setLabel() { attr.setLabel(true); encodeAttr(); } public boolean isDirectory() { return attr.isDirectory(); } public void setDirectory() { attr.setDirectory(true); encodeAttr(); } public boolean isArchive() { return attr.isArchive(); } public void setArchive() { attr.setArchive(true); encodeAttr(); } public byte[] getName() { return lName; } public void setName(byte[] value) { if (value.length != 11) throw new IllegalArgumentException("illegal shortname length: " + value.length); lName = value; encodeName(); } protected void clearName() { byte[] spaces = new byte[11]; Arrays.fill(spaces, 0, spaces.length, (byte) ' '); setName(spaces); } public String getBase() { return base; } public String getExt() { return ext; } public String getLabel() { String label; try { label = getFatFileSystem().getCodePage().newDecoder().decode(lName); } catch (CharacterCodingException ex) { log.debug("CharacterCodingException: CodePage error"); log.debug("go on with standard decoding"); label = new String(lName); } return label; } public String getShortName() { String base = getBase(); String ext = getExt(); if (ext.length() > 0) return base + "." + ext; else return base; } /* * checksum algorithm on page 28 the mask is to delete byte overwflow bits * Java has not unsigned types (sigh!) */ public byte getChkSum() { int sum = 0; for (int i = 0; i < 11; i++) { sum = (((sum & 1) == 1) ? 0x80 : 0) + (sum >> 1) + lName[i]; sum = sum & 0xFF; } return (byte) sum; } public long getCreated() { return created; } public void setCreated(long value) { created = FatUtils.checkDateTime(value); encodeCreated(); decodeCreated(); } public long getLastAccessed() { return accessed; } public void setLastAccessed(long value) { accessed = FatUtils.checkDateTime(value); encodeAccessed(); decodeAccessed(); } public long getLastModified() { return modified; } public void setLastModified(long value) { modified = FatUtils.checkDateTime(value); encodeModified(); decodeModified(); } public int getStartCluster() { return cluster; } public void setStartCluster(int value) { cluster = value; encodeCluster(); } public long getLength() { return length; } public void setLength(long value) { length = value; encodeLength(); } @Override public String toString() { return String.format( "Short Entry [%s] index:%d attr:%s size:%d", getShortName(), getIndex(), NumberUtils.hex(lAttr, 2), lFileSize); } public String toDebugString() { StrWriter out = new StrWriter(); out.println("*******************************************"); out.println("Short Entry\tisDirty[" + isDirty() + "]"); out.println("*******************************************"); out.println("Index\t\t" + getIndex()); out.println("Entry"); out.println(entry.getArray()); out.println("Name\t\t" + "<" + new String(lName) + ">"); out.println("Attr\t\t" + NumberUtils.hex(lAttr, 2)); out.println("NTRes\t\t" + NumberUtils.hex(lNTRes, 2)); out.println("CrtTimeTenth\t" + lCrtTimeTenth); out.println("CrtTime\t\t" + lCrtTime); out.println("CrtDate\t\t" + lCrtDate); out.println("LstAccDate\t" + lLstAccDate); out.println("FstClusHi\t" + lFstClusHi); out.println("WrtTime\t\t" + lWrtTime); out.println("WrtDate\t\t" + lWrtDate); out.println("FstClusLo\t" + lFstClusLo); out.println("FileSize\t" + lFileSize); out.println("*******************************************"); out.println("ShortName\t" + getShortName()); out.println("Attr\t\t" + getAttr()); out.println("Case\t\t" + getNameCase()); out.println("ChkSum\t\t" + NumberUtils.hex(getChkSum(), 2)); out.println("Created\t\t" + FatUtils.fTime(getCreated())); out.println("Accessed\t" + FatUtils.fDate(getLastAccessed())); out.println("Modified\t" + FatUtils.fTime(getLastModified())); out.println("StartCluster\t" + getStartCluster()); out.println("Length\t\t" + getLength()); out.print("*******************************************"); return out.toString(); } }