mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-09-28 17:11:11 -09:00
Merge remote-tracking branch 'origin/GP-807_dev747368_HFS_ISO9660'
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