/*- * Copyright (C) 2014 Erik Larsson * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.catacombae.storage.fs.hfscommon; import java.io.InputStream; import java.io.OutputStream; import java.util.LinkedList; import org.catacombae.hfs.io.ForkFilter; import org.catacombae.hfs.types.hfscommon.CommonHFSAttributesLeafRecord; import org.catacombae.hfs.types.hfscommon.CommonHFSExtentDescriptor; import org.catacombae.hfs.types.hfsplus.HFSPlusAttributesData; import org.catacombae.hfs.types.hfsplus.HFSPlusAttributesExtents; import org.catacombae.hfs.types.hfsplus.HFSPlusAttributesForkData; import org.catacombae.hfs.types.hfsplus.HFSPlusAttributesLeafRecordData; import org.catacombae.hfs.types.hfsplus.HFSPlusExtentDescriptor; import org.catacombae.hfs.types.hfsplus.HFSPlusExtentRecord; import org.catacombae.io.RandomAccessStream; import org.catacombae.io.ReadableByteArrayStream; import org.catacombae.io.ReadableRandomAccessInputStream; import org.catacombae.io.ReadableRandomAccessStream; import org.catacombae.io.SynchronizedReadableRandomAccessStream; import org.catacombae.io.TruncatableRandomAccessStream; import org.catacombae.io.WritableRandomAccessStream; import org.catacombae.storage.fs.FSFork; import org.catacombae.storage.fs.FSForkType; /** * @author <a href="http://www.catacombae.org/" target="_top">Erik Larsson</a> */ public class HFSCommonAttributeFork implements FSFork { private final HFSCommonFSEntry parent; private final CommonHFSAttributesLeafRecord[] recordList; HFSCommonAttributeFork(HFSCommonFSEntry parent, CommonHFSAttributesLeafRecord... recordList) { if(recordList.length == 0) { throw new RuntimeException("Empty record list!"); } this.parent = parent; this.recordList = recordList; } public FSForkType getType() { return null; } public long getLength() { final HFSPlusAttributesLeafRecordData firstRecordData = recordList[0].getRecordData(); final long length; if(firstRecordData instanceof HFSPlusAttributesData) { HFSPlusAttributesData attributesData = (HFSPlusAttributesData) firstRecordData; length = attributesData.getAttrSize(); } else if(firstRecordData instanceof HFSPlusAttributesForkData) { HFSPlusAttributesForkData attributesForkData = (HFSPlusAttributesForkData) firstRecordData; length = attributesForkData.getTheFork().getLogicalSize(); } else { throw new RuntimeException("Unexpected record type of first " + "record: " + firstRecordData.getClass()); } return length; } public long getOccupiedSize() { final HFSPlusAttributesLeafRecordData firstRecordData = recordList[0].getRecordData(); final long occupiedSize; if(firstRecordData instanceof HFSPlusAttributesData) { HFSPlusAttributesData attributesData = (HFSPlusAttributesData) firstRecordData; occupiedSize = attributesData.getAttrSize(); } else if(firstRecordData instanceof HFSPlusAttributesForkData) { HFSPlusAttributesForkData attributesForkData = (HFSPlusAttributesForkData) firstRecordData; occupiedSize = attributesForkData.getTheFork().getTotalBlocks() * parent.fsHandler.getFSView().getVolumeHeader(). getAllocationBlockSize(); } else { throw new RuntimeException("Unexpected record type of first " + "record: " + firstRecordData.getClass()); } return occupiedSize; } public boolean isWritable() { return false; } public boolean isTruncatable() { return false; } public boolean isCompressed() { return false; } public String getForkIdentifier() { return "Attribute: " + new String(recordList[0].getKey().getAttrName()); } public InputStream getInputStream() { return new ReadableRandomAccessInputStream( new SynchronizedReadableRandomAccessStream( getReadableRandomAccessStream())); } public ReadableRandomAccessStream getReadableRandomAccessStream() { final HFSPlusAttributesLeafRecordData firstRecordData = recordList[0].getRecordData(); final ReadableRandomAccessStream stream; if(firstRecordData instanceof HFSPlusAttributesData) { final HFSPlusAttributesData attributesData = (HFSPlusAttributesData) firstRecordData; stream = new ReadableByteArrayStream(attributesData.getAttrData()); } else if(firstRecordData instanceof HFSPlusAttributesForkData) { final HFSPlusAttributesForkData attributesForkData = (HFSPlusAttributesForkData) firstRecordData; LinkedList<CommonHFSExtentDescriptor> allExtents = new LinkedList<CommonHFSExtentDescriptor>(); HFSPlusExtentRecord curRecord = attributesForkData.getTheFork().getExtents(); int i = 0; long totalBlocks = 0; do { for(HFSPlusExtentDescriptor desc : curRecord.getExtentDescriptors()) { allExtents.addLast(CommonHFSExtentDescriptor.create(desc)); totalBlocks += desc.getBlockCount(); } ++i; curRecord = null; if(i < recordList.length) { final CommonHFSAttributesLeafRecord nextRecord = recordList[++i]; final HFSPlusAttributesLeafRecordData nextRecordData = nextRecord.getRecordData(); if(nextRecord.getKey().getStartBlock() != totalBlocks) { throw new RuntimeException("Unexpected start block " + "for record at index " + i + " (expected: " + totalBlocks + " actual: " + nextRecord.getKey().getStartBlock() + ")."); } if(nextRecordData instanceof HFSPlusAttributesExtents) { final HFSPlusAttributesExtents extentsData = (HFSPlusAttributesExtents) nextRecordData; curRecord = extentsData.getExtents(); } else { throw new RuntimeException("Unexpected attributes " + "leaf record type at index " + i + ": " + nextRecordData.getClass()); } } } while(curRecord != null); stream = new ForkFilter( attributesForkData.getTheFork().getLogicalSize(), allExtents.toArray(new CommonHFSExtentDescriptor[allExtents. size()]), parent.getFileSystemHandler().getFSView().createFSStream(), 0, parent.getFileSystemHandler().getFSView().getVolumeHeader(). getAllocationBlockSize(), 0); } else { throw new RuntimeException("Unexpected record type of first " + "record: " + firstRecordData.getClass()); } return stream; } public WritableRandomAccessStream getWritableRandomAccessStream() throws UnsupportedOperationException { throw new UnsupportedOperationException("Not supported yet."); } public RandomAccessStream getRandomAccessStream() throws UnsupportedOperationException { throw new UnsupportedOperationException("Not supported yet."); } public OutputStream getOutputStream() throws UnsupportedOperationException { throw new UnsupportedOperationException("Not supported yet."); } public TruncatableRandomAccessStream getForkStream() throws UnsupportedOperationException { throw new UnsupportedOperationException("Not supported yet."); } public boolean hasXattrName() { return true; } public String getXattrName() { return new String(recordList[0].getKey().getAttrName()); } }