/*- * Copyright (C) 2009 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.util.Arrays; import java.util.LinkedList; import java.util.List; import org.catacombae.hfs.AttributesFile; import org.catacombae.hfs.types.hfscommon.CommonHFSAttributesLeafRecord; import org.catacombae.util.Util; import org.catacombae.hfs.types.hfscommon.CommonHFSCatalogAttributes; import org.catacombae.hfs.types.hfscommon.CommonHFSCatalogNodeID; import org.catacombae.hfs.types.hfscommon.CommonHFSFinderInfo; import org.catacombae.storage.fs.BasicFSEntry; import org.catacombae.storage.fs.FSFork; import org.catacombae.storage.fs.FSForkType; import org.catacombae.storage.fs.FSLink; import org.catacombae.util.Util.Pair; /** * @author <a href="http://www.catacombae.org/" target="_top">Erik Larsson</a> */ public abstract class HFSCommonFSEntry extends BasicFSEntry { private static final char[] SECURITY_ATTRIBUTE_NAME = { 'c', 'o', 'm', '.', 'a', 'p', 'p', 'l', 'e', '.', 's', 'y', 's', 't', 'e', 'm', '.', 'S', 'e', 'c', 'u', 'r', 'i', 't', 'y', }; protected final HFSCommonFileSystemHandler fsHandler; protected final CommonHFSCatalogAttributes catalogAttributes; private FSFork finderInfoFork = null; private boolean finderInfoForkLoaded = false; LinkedList<FSFork> attributeForkList = null; protected HFSCommonFSEntry(HFSCommonFileSystemHandler parentFileSystem, CommonHFSCatalogAttributes catalogAttributes) { super(parentFileSystem); this.fsHandler = parentFileSystem; this.catalogAttributes = catalogAttributes; } protected synchronized void fillAttributeForks(List<FSFork> forkList) { if(attributeForkList == null) { LinkedList<FSFork> tmpAttributeForkList = new LinkedList<FSFork>(); AttributesFile attributesFile = fsHandler.getFSView().getAttributesFile(); if(attributesFile != null) { LinkedList<Pair<char[], LinkedList<CommonHFSAttributesLeafRecord>>> attributeBucketList = new LinkedList<Pair<char[], LinkedList<CommonHFSAttributesLeafRecord>>>(); for(CommonHFSAttributesLeafRecord attributeRecord : attributesFile.listAttributeRecords(getCatalogNodeID())) { Pair<char[], LinkedList<CommonHFSAttributesLeafRecord>> p; if(Arrays.equals(attributeRecord.getKey().getAttrName(), SECURITY_ATTRIBUTE_NAME)) { /* Skip the "com.apple.system.Security" attribute since * it contains access control lists and is supposed to * be hidden. */ continue; } if(!attributeBucketList.isEmpty() && (p = attributeBucketList.getLast()) != null) { if(Arrays.equals(p.getA(), attributeRecord.getKey().getAttrName())) { p.getB().addLast(attributeRecord); } } else { LinkedList<CommonHFSAttributesLeafRecord> bucket = new LinkedList<CommonHFSAttributesLeafRecord>(); bucket.add(attributeRecord); p = new Pair<char[], LinkedList<CommonHFSAttributesLeafRecord>>( attributeRecord.getKey().getAttrName(), bucket); attributeBucketList.add(p); } } for(Pair<char[], LinkedList<CommonHFSAttributesLeafRecord>> p : attributeBucketList) { LinkedList<CommonHFSAttributesLeafRecord> recordList = p.getB(); tmpAttributeForkList.add(new HFSCommonAttributeFork(this, recordList.toArray( new CommonHFSAttributesLeafRecord[recordList. size()]))); } } attributeForkList = tmpAttributeForkList; } forkList.addAll(attributeForkList); } HFSCommonFileSystemHandler getFileSystemHandler() { return fsHandler; } /* @Override */ public FSFork[] getAllForks() { LinkedList<FSFork> forkList = new LinkedList<FSFork>(); fillForks(forkList); return forkList.toArray(new FSFork[forkList.size()]); } protected void fillForks(List<FSFork> forkList) { FSFork fork = getFinderInfoFork(); if(fork != null) forkList.add(fork); FSFork resourceFork = getResourceFork(); if(resourceFork != null) { forkList.add(resourceFork); } fillAttributeForks(forkList); } /* @Override */ public FSFork getForkByType(FSForkType type) { if(type == FSForkType.MACOS_FINDERINFO) return getFinderInfoFork(); else return null; } /* @Override */ public long getCombinedLength() { FSFork fork = getFinderInfoFork(); if(fork != null) return finderInfoFork.getLength(); else return 0; } protected abstract CommonHFSCatalogNodeID getCatalogNodeID(); public FSFork getFinderInfoFork() { if(!finderInfoForkLoaded) { CommonHFSFinderInfo finderInfo = catalogAttributes.getFinderInfo(); byte[] finderInfoBytes = finderInfo.getBytes(); /* Imitating the Mac OS X hfs kernel driver, we zero the fields * 'document_id', 'date_added' and 'write_gen_counter' before * checking if whole struct is zeroed. */ Arrays.fill(finderInfoBytes, 16, 20, (byte) 0); Arrays.fill(finderInfoBytes, 20, 24, (byte) 0); Arrays.fill(finderInfoBytes, 28, 32, (byte) 0); /* Also, if this is a link then clear the type/creator fields in the * FinderInfo before comparing. */ if(this instanceof FSLink) { Arrays.fill(finderInfoBytes, 0, 4, (byte) 0); Arrays.fill(finderInfoBytes, 4, 8, (byte) 0); } if(!Util.zeroed(finderInfoBytes)) { finderInfoFork = new HFSCommonFinderInfoFork(finderInfo); } else finderInfoFork = null; finderInfoForkLoaded = true; } return finderInfoFork; } protected abstract FSFork getResourceFork(); }