mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-09-25 17:00:36 -09:00
GP-807 add support for HFS volumes and switch ISO9660 to 7zip
Both file systems are handled by existing 7zip, but HFS needed code to recognize the volume header. Remove ghidra implementation of ISO9660 as it lacked support for long file names and defer to the 7zip implementation.
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed 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 ghidra.file.formats.ios.hfs;
|
||||
|
||||
import ghidra.file.formats.sevenzip.SevenZipFileSystem;
|
||||
import ghidra.formats.gfilesystem.FSRLRoot;
|
||||
import ghidra.formats.gfilesystem.FileSystemService;
|
||||
import ghidra.formats.gfilesystem.annotations.FileSystemInfo;
|
||||
|
||||
@FileSystemInfo(type = "hfsplus", description = "Apple HFS+ Disk Volume", factory = HFSPlusFileSystemFactory.class)
|
||||
public class HFSPlusFileSystem extends SevenZipFileSystem {
|
||||
public HFSPlusFileSystem(FSRLRoot fsrl, FileSystemService fsService) {
|
||||
super(fsrl, fsService);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed 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 ghidra.file.formats.ios.hfs;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.formats.gfilesystem.FSRLRoot;
|
||||
import ghidra.formats.gfilesystem.FileSystemService;
|
||||
import ghidra.formats.gfilesystem.factory.GFileSystemFactoryByteProvider;
|
||||
import ghidra.formats.gfilesystem.factory.GFileSystemProbeByteProvider;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class HFSPlusFileSystemFactory
|
||||
implements GFileSystemFactoryByteProvider<HFSPlusFileSystem>, GFileSystemProbeByteProvider {
|
||||
|
||||
@Override
|
||||
public boolean probe(ByteProvider byteProvider, FileSystemService fsService,
|
||||
TaskMonitor monitor) throws IOException, CancelledException {
|
||||
return HFSPlusVolumeHeader.probe(byteProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HFSPlusFileSystem create(FSRLRoot targetFSRL, ByteProvider byteProvider,
|
||||
FileSystemService fsService, TaskMonitor monitor)
|
||||
throws IOException, CancelledException {
|
||||
|
||||
HFSPlusFileSystem fs = new HFSPlusFileSystem(targetFSRL, fsService);
|
||||
try {
|
||||
fs.mount(byteProvider, monitor);
|
||||
return fs;
|
||||
}
|
||||
catch (IOException ioe) {
|
||||
fs.close();
|
||||
throw ioe;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed 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 ghidra.file.formats.ios.hfs;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.app.util.bin.ByteProvider;
|
||||
|
||||
/**
|
||||
* Apple HFS+ volume header.
|
||||
* <p>
|
||||
* See https://developer.apple.com/library/archive/technotes/tn/tn1150.html#VolumeHeader
|
||||
* <p>
|
||||
* Fields are BigEndian
|
||||
*/
|
||||
public class HFSPlusVolumeHeader {
|
||||
//@formatter:off
|
||||
// Offset (hex) Length Comment
|
||||
private short signature; // 0 2 48 2b, "H+"
|
||||
private short version; // 2 2 4=HFS+, 5=HFSX
|
||||
private int attributes; // 4 4
|
||||
private int lastMountedVersion; // 8 4 '10.0' string
|
||||
private int journalInfoBlock; // C 4
|
||||
|
||||
private int createDate; // 10 4
|
||||
private int modifyDate; // 14 4
|
||||
private int backupDate; // 18 4
|
||||
private int checkedDate; // 1C 4
|
||||
|
||||
private int fileCount; // 20 4
|
||||
private int folderCount; // 24 4
|
||||
|
||||
private int blockSize; // 28 4 0x1000=4096
|
||||
private int totalBlocks; // 2C 4 blockSize*totalBlocks should equal vol size
|
||||
private int freeBlocks; // 30 4
|
||||
|
||||
private int nextAllocation; // 34 4
|
||||
private int rsrcClumpSize; // 38 4
|
||||
private int dataClumpSize; // 3C 4
|
||||
private int nextCatalogID; // 40 4
|
||||
|
||||
private int writeCount; // 44 4
|
||||
private long encodingsBitmap; // 48 8
|
||||
|
||||
private int[] finderInfo; // 50 32 uint32[8]
|
||||
|
||||
private byte[] rawForkData; // 70 400
|
||||
//HFSPlusForkData allocationFile; // 70 80
|
||||
//HFSPlusForkData extentsFile; // C0 80
|
||||
//HFSPlusForkData catalogFile; // 110 80
|
||||
//HFSPlusForkData attributesFile; // 160 80
|
||||
//HFSPlusForkData startupFile; // 1B0 80
|
||||
//@formatter:on
|
||||
|
||||
private static final int HFSPLUS_SIGNATURE_MAGIC = 0x482b; // "H+"
|
||||
private static final int HFSX_SIGNATURE_MAGIC = 0x4858; // "HX"
|
||||
private static final int HFSPLUS_VERSION = 4;
|
||||
private static final int HFSX_VERSION = 5;
|
||||
private static final int SIZEOF_HEADER = 512;
|
||||
private static final int DEFAULT_OFFSET = 1024;
|
||||
|
||||
public static boolean probe(ByteProvider provider) {
|
||||
try {
|
||||
if (provider.length() < DEFAULT_OFFSET + SIZEOF_HEADER) {
|
||||
return false;
|
||||
}
|
||||
HFSPlusVolumeHeader header = read(provider);
|
||||
return header.isValid() && header.hasGoodVolumeInfo(provider);
|
||||
}
|
||||
catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static HFSPlusVolumeHeader read(ByteProvider provider) throws IOException {
|
||||
return read(provider, DEFAULT_OFFSET);
|
||||
}
|
||||
|
||||
public static HFSPlusVolumeHeader read(ByteProvider provider, long offset) throws IOException {
|
||||
BinaryReader reader = new BinaryReader(provider, false /*BE*/);
|
||||
reader.setPointerIndex(offset);
|
||||
HFSPlusVolumeHeader result = new HFSPlusVolumeHeader();
|
||||
result.signature = reader.readNextShort();
|
||||
result.version = reader.readNextShort();
|
||||
result.attributes = reader.readNextInt();
|
||||
result.lastMountedVersion = reader.readNextInt();
|
||||
result.journalInfoBlock = reader.readNextInt();
|
||||
|
||||
result.createDate = reader.readNextInt();
|
||||
result.modifyDate = reader.readNextInt();
|
||||
result.backupDate = reader.readNextInt();
|
||||
result.checkedDate = reader.readNextInt();
|
||||
|
||||
result.fileCount = reader.readNextInt();
|
||||
result.folderCount = reader.readNextInt();
|
||||
|
||||
result.blockSize = reader.readNextInt();
|
||||
result.totalBlocks = reader.readNextInt();
|
||||
result.freeBlocks = reader.readNextInt();
|
||||
|
||||
result.nextAllocation = reader.readNextInt();
|
||||
result.rsrcClumpSize = reader.readNextInt();
|
||||
result.dataClumpSize = reader.readNextInt();
|
||||
result.nextCatalogID = reader.readNextInt();
|
||||
|
||||
result.writeCount = reader.readNextInt();
|
||||
result.encodingsBitmap = reader.readNextLong();
|
||||
|
||||
result.finderInfo = reader.readNextIntArray(8);
|
||||
|
||||
result.rawForkData = reader.readNextByteArray(400);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean isValid() {
|
||||
return signature == HFSPLUS_SIGNATURE_MAGIC && version == HFSPLUS_VERSION &&
|
||||
isGoodBlockSize(blockSize);
|
||||
}
|
||||
|
||||
private static boolean isGoodBlockSize(int bs) {
|
||||
return bs > 0 && bs % 512 == 0;
|
||||
}
|
||||
|
||||
public boolean hasGoodVolumeInfo(ByteProvider bp) throws IOException {
|
||||
long calculatedSize = blockSize * totalBlocks;
|
||||
// NOTE: can't compare with exact equals-to provider size because an extra 16 bytes
|
||||
// are present in examples extracted from firmware images
|
||||
return bp.length() >= calculatedSize;
|
||||
}
|
||||
}
|
||||
@@ -15,283 +15,15 @@
|
||||
*/
|
||||
package ghidra.file.formats.iso9660;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.formats.gfilesystem.*;
|
||||
import ghidra.file.formats.sevenzip.SevenZipFileSystem;
|
||||
import ghidra.formats.gfilesystem.FSRLRoot;
|
||||
import ghidra.formats.gfilesystem.FileSystemService;
|
||||
import ghidra.formats.gfilesystem.annotations.FileSystemInfo;
|
||||
import ghidra.formats.gfilesystem.factory.GFileSystemBaseFactory;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.*;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
@FileSystemInfo(type = "iso9660", description = "ISO 9660", factory = GFileSystemBaseFactory.class)
|
||||
public class ISO9660FileSystem extends GFileSystemBase {
|
||||
@FileSystemInfo(type = "iso9660", description = "ISO 9660", factory = ISO9660FileSystemFactory.class)
|
||||
public class ISO9660FileSystem extends SevenZipFileSystem {
|
||||
|
||||
//Possible locations for magic number
|
||||
private static final long[] SIGNATURE_PROBE_OFFSETS = new long[] { 0x8000L, 0x8800L, 0x9000L };
|
||||
|
||||
//Location where the magic number was found
|
||||
private long signatureOffset;
|
||||
|
||||
//Set true if the root level directory has been processed
|
||||
private boolean lookedAtRoot = false;
|
||||
|
||||
private short logicalBlockSize;
|
||||
|
||||
private ISO9660Header header;
|
||||
|
||||
private Map<GFile, ISO9660Directory> fileToDirectoryMap = new HashMap<>();
|
||||
|
||||
public ISO9660FileSystem(String fileSystemName, ByteProvider provider) {
|
||||
|
||||
super(fileSystemName, provider);
|
||||
public ISO9660FileSystem(FSRLRoot fsrl, FileSystemService fsService) {
|
||||
super(fsrl, fsService);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(TaskMonitor monitor) throws IOException {
|
||||
for (long probeOffset : SIGNATURE_PROBE_OFFSETS) {
|
||||
if (isMagicSignatureAt(probeOffset + 1)) {
|
||||
// signature is at +1 offset from the start of the volume offset
|
||||
signatureOffset = probeOffset;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isMagicSignatureAt(long offset) throws IOException {
|
||||
int magicLen = ISO9660Constants.MAGIC_BYTES.length;
|
||||
long providerLen = provider.length();
|
||||
return (providerLen > offset + magicLen) &&
|
||||
Arrays.equals(provider.readBytes(offset, magicLen), ISO9660Constants.MAGIC_BYTES);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void open(TaskMonitor monitor) throws IOException, CryptoException, CancelledException {
|
||||
BinaryReader reader = new BinaryReader(provider, true);
|
||||
|
||||
//Set start of pointer index of beginning of primary volume descriptor
|
||||
reader.setPointerIndex(signatureOffset);
|
||||
|
||||
header = new ISO9660Header(reader);
|
||||
ISO9660VolumeDescriptor pvd = header.getPrimaryVolumeDescriptor();
|
||||
logicalBlockSize = pvd.getLogicalBlockSizeBE();
|
||||
ISO9660Directory rootDir = header.getPrimaryDirectory();
|
||||
|
||||
//Get the list containing all directories at the root level of the file system
|
||||
List<ISO9660Directory> topLevel =
|
||||
createDirectoryList(reader, rootDir, pvd.getLogicalBlockSizeLE(), monitor);
|
||||
|
||||
try {
|
||||
|
||||
//Entry point for the this recursive function to process all nested
|
||||
//directories
|
||||
createDirectories(reader, topLevel, pvd.getLogicalBlockSizeLE(), monitor);
|
||||
}
|
||||
|
||||
catch (Exception e) {
|
||||
Msg.showError(this, null, "Directory Creation Error",
|
||||
"Failed to create archive directories");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
super.close();
|
||||
header = null;
|
||||
fileToDirectoryMap.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GFile> getListing(GFile directory) throws IOException {
|
||||
if (directory == null || directory.equals(root)) {
|
||||
List<GFile> roots = new ArrayList<>();
|
||||
for (GFile file : fileToDirectoryMap.keySet()) {
|
||||
if (file.getParentFile() == root || file.getParentFile().equals(root)) {
|
||||
roots.add(file);
|
||||
}
|
||||
}
|
||||
return roots;
|
||||
}
|
||||
List<GFile> tmp = new ArrayList<>();
|
||||
for (GFile file : fileToDirectoryMap.keySet()) {
|
||||
if (file.getParentFile() == null) {
|
||||
continue;
|
||||
}
|
||||
if (file.getParentFile().equals(directory)) {
|
||||
tmp.add(file);
|
||||
}
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteProvider getByteProvider(GFile file, TaskMonitor monitor)
|
||||
throws IOException, CancelledException {
|
||||
ISO9660Directory dir = fileToDirectoryMap.get(file);
|
||||
return dir.getByteProvider(provider, logicalBlockSize, file.getFSRL());
|
||||
}
|
||||
|
||||
/*
|
||||
* From a given parent directory create each child directory
|
||||
* under that parent directory and add them to a list
|
||||
*/
|
||||
private List<ISO9660Directory> createDirectoryList(BinaryReader reader,
|
||||
ISO9660Directory parentDir, long blockSize, TaskMonitor monitor) throws IOException {
|
||||
|
||||
List<ISO9660Directory> directoryList = new ArrayList<>();
|
||||
ISO9660Directory childDir = null;
|
||||
long dirIndex;
|
||||
long endIndex;
|
||||
|
||||
//Get location from parent into child directory
|
||||
dirIndex = parentDir.getLocationOfExtentLE() * blockSize;
|
||||
endIndex = dirIndex + parentDir.getDataLengthLE();
|
||||
|
||||
//while there is still more data in the current directory level
|
||||
while (dirIndex < endIndex) {
|
||||
reader.setPointerIndex(dirIndex);
|
||||
|
||||
//If the next byte is not zero then create the directory
|
||||
if (reader.peekNextByte() != 0) {
|
||||
if (!lookedAtRoot) {
|
||||
childDir = new ISO9660Directory(reader);
|
||||
addAndStoreDirectory(monitor, directoryList, childDir);
|
||||
|
||||
}
|
||||
|
||||
//Root level has already been looked at
|
||||
else {
|
||||
if (parentDir.getName() != null) {
|
||||
childDir = new ISO9660Directory(reader, parentDir);
|
||||
addAndStoreDirectory(monitor, directoryList, childDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Otherwise there is a gap in the data so keep looking forward
|
||||
//while still under the end index and create directory when data is
|
||||
//reached
|
||||
else {
|
||||
readWhileZero(reader, endIndex);
|
||||
|
||||
//Create the data once the reader finds the next position
|
||||
//and not reached end index
|
||||
if (reader.getPointerIndex() < endIndex) {
|
||||
if (!lookedAtRoot) {
|
||||
childDir = new ISO9660Directory(reader);
|
||||
addAndStoreDirectory(monitor, directoryList, childDir);
|
||||
dirIndex = childDir.getVolumeIndex();
|
||||
}
|
||||
else {
|
||||
if (parentDir.getName() != null) {
|
||||
childDir = new ISO9660Directory(reader, parentDir);
|
||||
addAndStoreDirectory(monitor, directoryList, childDir);
|
||||
dirIndex = childDir.getVolumeIndex();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
dirIndex += childDir.getDirectoryRecordLength();
|
||||
}
|
||||
|
||||
lookedAtRoot = true;
|
||||
return directoryList;
|
||||
}
|
||||
|
||||
private void readWhileZero(BinaryReader reader, long endIndex) throws IOException {
|
||||
while (reader.peekNextByte() == 0) {
|
||||
|
||||
//keep reading if all zeros until non zero is met or
|
||||
//end index reached
|
||||
if (reader.getPointerIndex() < endIndex) {
|
||||
reader.readNextByte();
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addAndStoreDirectory(TaskMonitor monitor, List<ISO9660Directory> directoryList,
|
||||
ISO9660Directory childDir) {
|
||||
|
||||
directoryList.add(childDir);
|
||||
|
||||
if (childDir.getName() != null) {
|
||||
storeDirectory(childDir, monitor);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Recurses though each level of a directory structure
|
||||
* in a depth-first manner
|
||||
* and creates each directory also marking them in the binary
|
||||
*/
|
||||
private void createDirectories(BinaryReader reader, List<ISO9660Directory> directoryList,
|
||||
long blockSize, TaskMonitor monitor) throws DuplicateNameException, Exception {
|
||||
|
||||
for (ISO9660Directory dir : directoryList) {
|
||||
|
||||
//If the directory is a new level of directories
|
||||
//recurse down into the next level
|
||||
if (dir.isDirectoryFlagSet() && dir.getName() != null) {
|
||||
List<ISO9660Directory> dirs;
|
||||
dirs = createDirectoryList(reader, dir, blockSize, monitor);
|
||||
createDirectories(reader, dirs, blockSize, monitor);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Stores a gFile after finding its parent and its matching directory
|
||||
*/
|
||||
private void storeDirectory(ISO9660Directory directory, TaskMonitor monitor) {
|
||||
|
||||
String dirName = directory.getName();
|
||||
boolean isDirectory = directory.isDirectoryFlagSet();
|
||||
int length = directory.getDataLengthLE();
|
||||
GFileImpl gFile = null;
|
||||
|
||||
//Map does not contain entries yet since root level needs to be processed
|
||||
if (!lookedAtRoot) {
|
||||
gFile = GFileImpl.fromFilename(this, root, dirName, isDirectory, length, null);
|
||||
storeFile(gFile, directory);
|
||||
}
|
||||
else {
|
||||
//Root has been processed, all other entries must have a parent
|
||||
String parentDirName = directory.getParentDirectory().getName();
|
||||
for (GFile currGFile : fileToDirectoryMap.keySet()) {
|
||||
//Find the parent and store the file
|
||||
if (parentDirName.equals(currGFile.getName())) {
|
||||
gFile =
|
||||
GFileImpl.fromFilename(this, currGFile, dirName, isDirectory, length, null);
|
||||
storeFile(gFile, directory);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void storeFile(GFile file, ISO9660Directory directory) {
|
||||
if (file == null) {
|
||||
return;
|
||||
}
|
||||
if (file.equals(root)) {
|
||||
return;
|
||||
}
|
||||
if (!fileToDirectoryMap.containsKey(file) || fileToDirectoryMap.get(file) == null) {
|
||||
fileToDirectoryMap.put(file, directory);
|
||||
}
|
||||
GFile parentFile = file.getParentFile();
|
||||
storeFile(parentFile, null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed 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 ghidra.file.formats.iso9660;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.formats.gfilesystem.FSRLRoot;
|
||||
import ghidra.formats.gfilesystem.FileSystemService;
|
||||
import ghidra.formats.gfilesystem.factory.GFileSystemFactoryByteProvider;
|
||||
import ghidra.formats.gfilesystem.factory.GFileSystemProbeByteProvider;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class ISO9660FileSystemFactory
|
||||
implements GFileSystemFactoryByteProvider<ISO9660FileSystem>, GFileSystemProbeByteProvider {
|
||||
private static final long[] SIGNATURE_PROBE_OFFSETS = new long[] { 0x8000L, 0x8800L, 0x9000L };
|
||||
|
||||
@Override
|
||||
public boolean probe(ByteProvider byteProvider, FileSystemService fsService,
|
||||
TaskMonitor monitor) throws IOException, CancelledException {
|
||||
|
||||
for (long probeOffset : SIGNATURE_PROBE_OFFSETS) {
|
||||
if (isMagicSignatureAt(byteProvider, probeOffset + 1)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isMagicSignatureAt(ByteProvider provider, long offset) throws IOException {
|
||||
int magicLen = ISO9660Constants.MAGIC_BYTES.length;
|
||||
long providerLen = provider.length();
|
||||
return (providerLen > offset + magicLen) &&
|
||||
Arrays.equals(provider.readBytes(offset, magicLen), ISO9660Constants.MAGIC_BYTES);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ISO9660FileSystem create(FSRLRoot targetFSRL, ByteProvider byteProvider,
|
||||
FileSystemService fsService, TaskMonitor monitor)
|
||||
throws IOException, CancelledException {
|
||||
|
||||
ISO9660FileSystem fs = new ISO9660FileSystem(targetFSRL, fsService);
|
||||
try {
|
||||
fs.mount(byteProvider, monitor);
|
||||
return fs;
|
||||
}
|
||||
catch (IOException ioe) {
|
||||
fs.close();
|
||||
throw ioe;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user