blob: 6e23de093b3e071720f6312000afede5c5ce22bd [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.flex.utilities.converter.retrievers.download.utils.utils;
import java.io.DataInputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.Date;
/**
* http://www.dubeyko.com/development/FileSystems/HFSPLUS/hexdumps/hfsplus_volume_header.html
* http://dubeiko.com/development/FileSystems/HFSPLUS/tn1150.html
* http://dubeyko.com/development/FileSystems/HFSPLUS/tn1150.html#CatalogFile
*
* Created by christoferdutz on 24.02.16.
*/
public class HFSPlusVolumeHeader implements IHFSVolumeHeader {
private static final int VOLUME_UNMOUNTED = 0x0100;
private static final int VOLUME_SPARED_BLOCKS = 0x0200;
private static final int VOLUME_NOCACHE_REQUIRED = 0x0400;
private static final int BOOT_VOLUME_INCONSISTENT = 0x0800;
private static final int CATALOG_NODE_IDS_REUSED = 0x1000;
private static final int VOLUME_JOURNALED = 0x2000;
private static final int VOLUME_SOFTWARE_LOCK= 0x8000;
private static final long UNSIGNED_INT_BITS = 0xFFFFFFFFL;
private static final long CONVERT_MAC_TO_JAVA_TIME = 2082880800L;
private short version;
private int attributes;
private int lastMountedVersion;
private int journalInfoBlock;
private Date createDate;
private Date modifyDate;
private Date backupDate;
private Date checkedDate;
private int fileCount;
private int folderCount;
private int blockSize;
private int totalBlocks;
private int freeBlocks;
private int nextAllocation;
private int rsrcClumpSize;
private int nextCatalogID;
private int dataClumpSize;
private int writeCount;
private long encodingsBitmap;
private HFSPlusFinderInfo finderInfo;
private HFSPlusForkData allocationFile;
private HFSPlusForkData extentsFile;
private HFSPlusForkData catalogFile;
private HFSPlusForkData attributesFile;
private HFSPlusForkData startupFile;
public HFSPlusVolumeHeader(DataInputStream dis) {
try {
version = dis.readShort();
attributes = dis.readInt();
lastMountedVersion = dis.readInt();
journalInfoBlock = dis.readInt();
createDate = readMacDate(dis);
modifyDate = readMacDate(dis);
backupDate = readMacDate(dis);
checkedDate = readMacDate(dis);
fileCount = dis.readInt();
folderCount = dis.readInt();
blockSize = dis.readInt();
totalBlocks = dis.readInt();
freeBlocks = dis.readInt();
nextAllocation = dis.readInt();
rsrcClumpSize = dis.readInt();
dataClumpSize = dis.readInt();
nextCatalogID = dis.readInt();
writeCount = dis.readInt();
encodingsBitmap = dis.readLong();
finderInfo = new HFSPlusFinderInfo(dis);
allocationFile = new HFSPlusForkData(dis);
extentsFile = new HFSPlusForkData(dis);
catalogFile = new HFSPlusForkData(dis);
attributesFile = new HFSPlusForkData(dis);
startupFile = new HFSPlusForkData(dis);
} catch (IOException e) {
throw new IllegalArgumentException("Invalid HFSPlusVolumeHeader data.");
}
}
public short getVersion() {
return version;
}
public int getAttributes() {
return attributes;
}
/**
* @return true if the volume was correctly flushed before being unmounted or ejected.
*/
public boolean isVolumeUnmounted() {
return (attributes & VOLUME_UNMOUNTED) != 0;
}
/**
* @return true if there are any records in the extents overflow file for bad blocks
* (belonging to file ID kHFSBadBlockFileID).
*/
public boolean isSparedBlocks() {
return (attributes & VOLUME_SPARED_BLOCKS) != 0;
}
/**
* @return true if the blocks from this volume should not be cached.
*/
public boolean isNoCacheRequired() {
return (attributes & VOLUME_NOCACHE_REQUIRED) != 0;
}
/**
* @return true if the volume was NOT correctly flushed before being unmounted or ejected.
*/
public boolean isBootVolumeInconsistent() {
return (attributes & BOOT_VOLUME_INCONSISTENT) != 0;
}
/**
* @return true when the nextCatalogID field overflows 32 bits, forcing smaller catalog node
* IDs to be reused. When this bit is set, it is common (and not an error) for catalog records
* to exist with IDs greater than or equal to nextCatalogID.
*/
public boolean isCatalogIdsReused() {
return (attributes & CATALOG_NODE_IDS_REUSED) != 0;
}
/**
* @return true if the volume has a journal.*2
*
*/
public boolean isVolumeJournaled() {
return (attributes & VOLUME_JOURNALED) != 0;
}
/**
* @return true if the volume is write-protected due to a software setting. Any implementations
* must refuse to write to a volume with this bit set.
*/
public boolean isVolumeSoftwareLocked() {
return (attributes & VOLUME_SOFTWARE_LOCK) != 0;
}
public int getLastMountedVersion() {
return lastMountedVersion;
}
public int getJournalInfoBlock() {
return journalInfoBlock;
}
public Date getCreateDate() {
return createDate;
}
public Date getModifyDate() {
return modifyDate;
}
public Date getBackupDate() {
return backupDate;
}
public Date getCheckedDate() {
return checkedDate;
}
public int getFileCount() {
return fileCount;
}
public int getFolderCount() {
return folderCount;
}
public int getBlockSize() {
return blockSize;
}
public int getTotalBlocks() {
return totalBlocks;
}
public int getFreeBlocks() {
return freeBlocks;
}
public int getNextAllocation() {
return nextAllocation;
}
public int getRsrcClumpSize() {
return rsrcClumpSize;
}
public int getDataClumpSize() {
return dataClumpSize;
}
public int getWriteCount() {
return writeCount;
}
public long getEncodingsBitmap() {
return encodingsBitmap;
}
public int getNextCatalogID() {
return nextCatalogID;
}
public HFSPlusFinderInfo getFinderInfo() {
return finderInfo;
}
public HFSPlusForkData getAllocationFile() {
return allocationFile;
}
public HFSPlusForkData getExtentsFile() {
return extentsFile;
}
public HFSPlusForkData getCatalogFile() {
return catalogFile;
}
public HFSPlusForkData getAttributesFile() {
return attributesFile;
}
public HFSPlusForkData getStartupFile() {
return startupFile;
}
/**
* Convert the dates saved as HFS+ date (32 bit integer representing the number
* of seconds since 01.01.1994) to java dates.
* @param dis input stream to read from.
* @return Date in Java representation (number of milliseconds since 01.01.1970)
* @throws IOException something went wrong.
*/
private Date readMacDate(DataInputStream dis) throws IOException {
return new Date(((UNSIGNED_INT_BITS & dis.readInt()) - CONVERT_MAC_TO_JAVA_TIME) * 1000);
}
@Override
public String toString() {
return "HFSPlusVolumeHeader{" +
"version=" + version +
", attributes=" + attributes +
", lastMountedVersion=" + lastMountedVersion +
", journalInfoBlock=" + journalInfoBlock +
", createDate=" + createDate +
", modifyDate=" + modifyDate +
", backupDate=" + backupDate +
", checkedDate=" + checkedDate +
", fileCount=" + fileCount +
", folderCount=" + folderCount +
", blockSize=" + blockSize +
", totalBlocks=" + totalBlocks +
", freeBlocks=" + freeBlocks +
", nextAllocation=" + nextAllocation +
", rsrcClumpSize=" + rsrcClumpSize +
", nextCatalogID=" + nextCatalogID +
", dataClumpSize=" + dataClumpSize +
", writeCount=" + writeCount +
", encodingsBitmap=" + encodingsBitmap +
", finderInfo=" + finderInfo +
", allocationFile=" + allocationFile +
", extentsFile=" + extentsFile +
", catalogFile=" + catalogFile +
", attributesFile=" + attributesFile +
", startupFile=" + startupFile +
'}';
}
}