/*
* $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.hfsplus;
import java.io.IOException;
import org.jnode.driver.ApiNotFoundException;
import org.jnode.fs.FileSystemException;
public class HFSPlusParams {
public static final int MINIMAL_BLOCK_SIZE = 512;
public static final int DEFAULT_BLOCK_SIZE = 4096;
public static final int OPTIMAL_BLOCK_SIZE = 4096;
public static final int DATA_CLUMP_FACTOR = 16;
public static final int RESOURCE_CLUMP_FACTOR = 16;
public static final int DEFAULT_JOURNAL_SIZE = 8 * 1024 * 1024;
public static final int DEFAULT_CATALOG_NODE_SIZE = 8192;
public static final int DEFAULT_EXTENT_NODE_SIZE = 4096;
public static final int DEFAULT_ATTRIBUTE_NODE_SIZE = 4096;
private long blockDeviceSize;
private String volumeName;
private int blockSize;
private int resourceClumpBlocks;
private int dataClumpBlocks;
private int catalogClumpBlocks;
private int extentClumpBlocks;
private int attributeClumpBlocks;
private int bitmapClumpBlocks;
private boolean journaled;
private int journalSize;
private int resourceClumpSize;
private int dataClumpSize;
private int catalogClumpSize;
private int catalogNodeSize;
private int extentClumpSize;
private int extentNodeSize;
private int attributeClumpSize;
private int attributeNodeSize;
private int allocationClumpSize;
/**
* Default constructor.
*/
public HFSPlusParams() {
this.volumeName = "";
this.blockSize = DEFAULT_BLOCK_SIZE;
this.catalogNodeSize = DEFAULT_CATALOG_NODE_SIZE;
this.extentNodeSize = DEFAULT_EXTENT_NODE_SIZE;
}
/**
* Initialize default sizes (allocation, catalog node, etc...)
*
* @param fs
* @throws FileSystemException
* @throws IOException
*/
public void initializeDefaultsValues(HfsPlusFileSystem fs)
throws FileSystemException, IOException {
try {
long clumpSize = 0;
this.blockDeviceSize = fs.getApi().getLength();
if (resourceClumpBlocks == 0) {
if (blockSize > DEFAULT_BLOCK_SIZE) {
clumpSize = round(RESOURCE_CLUMP_FACTOR * DEFAULT_BLOCK_SIZE, blockSize);
} else {
clumpSize = RESOURCE_CLUMP_FACTOR * blockSize;
}
} else {
clumpSize = clumpSizeCalculation(resourceClumpBlocks);
}
resourceClumpSize = (int) clumpSize;
if (dataClumpBlocks == 0) {
if (blockSize > DEFAULT_BLOCK_SIZE) {
clumpSize = round(DATA_CLUMP_FACTOR * DEFAULT_BLOCK_SIZE, blockSize);
} else {
clumpSize = DATA_CLUMP_FACTOR * blockSize;
}
} else {
clumpSize = clumpSizeCalculation(dataClumpBlocks);
}
if (blockSize < OPTIMAL_BLOCK_SIZE || blockDeviceSize < 0x40000000) {
catalogNodeSize = 4096;
}
long sectorCount = blockDeviceSize / fs.getFSApi().getSectorSize();
if (catalogClumpBlocks == 0) {
clumpSize = getBTreeClumpSize(blockSize, catalogNodeSize, sectorCount, true);
} else {
clumpSize = clumpSizeCalculation(catalogClumpBlocks);
if (clumpSize % catalogNodeSize != 0) {
throw new FileSystemException("clump size is not a multiple of node size");
}
}
catalogClumpSize = (int) clumpSize;
if (extentClumpBlocks == 0) {
clumpSize = getBTreeClumpSize(blockSize, extentNodeSize, sectorCount, false);
} else {
clumpSize = clumpSizeCalculation(extentClumpBlocks);
}
extentClumpSize = (int) clumpSize;
if (attributeClumpBlocks == 0) {
clumpSize = 0;
} else {
clumpSize = clumpSizeCalculation(attributeClumpBlocks);
if (clumpSize % attributeNodeSize != 0) {
throw new FileSystemException(
"clump size is not a multiple of attribute node size");
}
}
attributeClumpSize = (int) clumpSize;
long totalBlocks = this.getBlockCount();
long minClumpSize = this.getBlockCount() >> 3;
if ((totalBlocks & 7) == 0) {
++minClumpSize;
}
if (bitmapClumpBlocks == 0) {
clumpSize = minClumpSize;
} else {
clumpSize = clumpSizeCalculation(bitmapClumpBlocks);
if (clumpSize < minClumpSize) {
throw new FileSystemException("bitmap clump size is too small.");
}
}
allocationClumpSize = (int) clumpSize;
} catch (ApiNotFoundException e) {
throw new FileSystemException("Unable initialize default values.", e);
}
}
private int[] extentClumpTable = new int[]{4, 4, 4, 5, 5, 6, 7, 8, 9, 11, 14, 16, 20, 25, 32};
private int[] catalogClumpTable =
new int[]{4, 6, 8, 11, 14, 19, 25, 34, 45, 60, 80, 107, 144, 192, 256};
/**
* Get the file clump size for Extent and catalog B-Tree files.
*
* @param blockSize Size of a block.
* @param nodeSize Size of a node.
* @param sectors Number of sector for the device.
* @param catalog If true, calculate catalog clump size. In the other case,
* calculate extent clump size.
* @return B-Tree clump size.
*/
private long getBTreeClumpSize(int blockSize, int nodeSize, long sectors, boolean catalog) {
int size = Math.max(blockSize, nodeSize);
long clumpSize = 0;
if (sectors < 0x200000) {
clumpSize = (sectors << 2);
if (clumpSize < (8 * nodeSize)) {
clumpSize = (8 * nodeSize);
}
} else {
sectors = sectors >> 22;
for (int i = 0; sectors != 0 && (i < 14); ++i) {
if (catalog) {
clumpSize = catalogClumpTable[i] * 1024 * 1024;
} else {
clumpSize = extentClumpTable[i] * 1024 * 1024;
}
sectors = sectors >> 1;
}
}
clumpSize /= size;
clumpSize *= size;
if (clumpSize == 0) {
clumpSize = size;
}
return clumpSize;
}
/**
* @param clumpBlocks
* @return
*/
private int clumpSizeCalculation(long clumpBlocks) throws FileSystemException {
long clumpSize = clumpBlocks * blockSize;
if ((clumpSize & 0XFFFFFFFF00000000L) == 0) {
throw new FileSystemException("Too many blocks (" + clumpBlocks + ") for clump size (" +
clumpSize + ").");
}
return (int) clumpSize;
}
private long round(long x, long y) {
return (((x + y) - 1) / y * y);
}
public String getVolumeName() {
return volumeName;
}
public void setVolumeName(String volumeName) {
this.volumeName = volumeName;
}
public int getBlockSize() {
return blockSize;
}
public void setBlockSize(int blockSize) {
this.blockSize = blockSize;
}
public boolean isJournaled() {
return journaled;
}
public void setJournaled(boolean journaled) {
this.journaled = journaled;
}
public int getJournalSize() {
return journalSize;
}
public void setJournalSize(int journalSize) {
this.journalSize = journalSize;
}
public int getCatalogNodeSize() {
return catalogNodeSize;
}
public long getBlockCount() {
return blockDeviceSize / blockSize;
}
public int getCatalogClumpSize() {
return catalogClumpSize;
}
public int getExtentClumpSize() {
return extentClumpSize;
}
public int getResourceClumpSize() {
return resourceClumpSize;
}
public int getDataClumpSize() {
return dataClumpSize;
}
public int getAttributeClumpSize() {
return attributeClumpSize;
}
public int getAttributeNodeSize() {
return attributeNodeSize;
}
public void setAttributeClumpBlocks(int attributeClumpBlocks) {
this.attributeClumpBlocks = attributeClumpBlocks;
}
public int getAllocationClumpSize() {
return allocationClumpSize;
}
public void setBitmapClumpBlocks(int bitmapClumpBlocks) {
this.bitmapClumpBlocks = bitmapClumpBlocks;
}
public int getExtentNodeSize() {
return extentNodeSize;
}
public int getInitializeNumRecords() {
return journaled ? 6 : 2;
}
}