mirror of
https://github.com/aaru-dps/Aaru.git
synced 2025-12-16 19:24:25 +00:00
* DiscImageChef/Main.cs:
* DiscImageChef/Options.cs: * DiscImageChef/DiscImageChef.csproj: * DiscImageChef/Commands/ExtractFiles.cs: Added command to extract all files from a filesystem. * DiscImageChef.Filesystems/LisaFS/Consts.cs: Corrected comments. Added ftype known values. * DiscImageChef.Filesystems/LisaFS/Dir.cs: Changed field name. * DiscImageChef.Filesystems/LisaFS/Extent.cs: * DiscImageChef.Filesystems/LisaFS/Structs.cs: Reverse engineered new fields from ExtentsFile * DiscImageChef.Filesystems/LisaFS/File.cs: Added support for reading tags. Added flags and ftype fields from ExtentsFile. * DiscImageChef.Filesystems/LisaFS/Xattr.cs: Changed how serial number is returned. Allow to get tags in debug mode as an xattr. * DiscImageChef.Filesystems/Structs.cs: Added PIPE attribute.
This commit is contained in:
@@ -44,20 +44,40 @@ namespace DiscImageChef.Filesystems.LisaFS
|
|||||||
const byte LisaFSv1 = 0x0E;
|
const byte LisaFSv1 = 0x0E;
|
||||||
const byte LisaFSv2 = 0x0F;
|
const byte LisaFSv2 = 0x0F;
|
||||||
const byte LisaFSv3 = 0x11;
|
const byte LisaFSv3 = 0x11;
|
||||||
|
/// <summary>Maximum string size in LisaFS</summary>
|
||||||
const uint E_NAME = 32;
|
const uint E_NAME = 32;
|
||||||
// Maximum string size in LisaFS
|
|
||||||
const UInt16 FILEID_FREE = 0x0000;
|
const UInt16 FILEID_FREE = 0x0000;
|
||||||
const UInt16 FILEID_BOOT = 0xAAAA;
|
const UInt16 FILEID_BOOT = 0xAAAA;
|
||||||
const UInt16 FILEID_LOADER = 0xBBBB;
|
const UInt16 FILEID_LOADER = 0xBBBB;
|
||||||
const UInt16 FILEID_MDDF = 0x0001;
|
const UInt16 FILEID_MDDF = 0x0001;
|
||||||
const UInt16 FILEID_BITMAP = 0x0002;
|
const UInt16 FILEID_BITMAP = 0x0002;
|
||||||
const UInt16 FILEID_SRECORD = 0x0003;
|
const UInt16 FILEID_SRECORD = 0x0003;
|
||||||
|
/// <summary>"Catalog file"</summary>
|
||||||
const UInt16 FILEID_DIRECTORY = 0x0004;
|
const UInt16 FILEID_DIRECTORY = 0x0004;
|
||||||
const Int16 FILEID_BOOT_SIGNED = -21846;
|
const Int16 FILEID_BOOT_SIGNED = -21846;
|
||||||
const Int16 FILEID_LOADER_SIGNED = -17477;
|
const Int16 FILEID_LOADER_SIGNED = -17477;
|
||||||
// "Catalog file"
|
|
||||||
const UInt16 FILEID_ERASED = 0x7FFF;
|
const UInt16 FILEID_ERASED = 0x7FFF;
|
||||||
const UInt16 FILEID_MAX = FILEID_ERASED;
|
const UInt16 FILEID_MAX = FILEID_ERASED;
|
||||||
|
|
||||||
|
enum FileType : byte
|
||||||
|
{
|
||||||
|
Undefined = 0,
|
||||||
|
MDDFile = 1,
|
||||||
|
RootCat = 2,
|
||||||
|
FreeList = 3,
|
||||||
|
BadBlocks = 4,
|
||||||
|
SysData = 5,
|
||||||
|
Spool = 6,
|
||||||
|
Exec = 7,
|
||||||
|
UserCat = 8,
|
||||||
|
Pipe = 9,
|
||||||
|
BootFile = 10,
|
||||||
|
SwapData = 11,
|
||||||
|
SwapCode = 12,
|
||||||
|
RamAP = 13,
|
||||||
|
UserFile = 14,
|
||||||
|
KilledObject = 15
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -147,7 +147,7 @@ namespace DiscImageChef.Filesystems.LisaFS
|
|||||||
entry.zero = BigEndianBitConverter.ToUInt16(buf, offset + 0x01);
|
entry.zero = BigEndianBitConverter.ToUInt16(buf, offset + 0x01);
|
||||||
entry.filename = new byte[E_NAME];
|
entry.filename = new byte[E_NAME];
|
||||||
Array.Copy(buf, offset + 0x03, entry.filename, 0, E_NAME);
|
Array.Copy(buf, offset + 0x03, entry.filename, 0, E_NAME);
|
||||||
entry.padding = buf[offset + 0x23];
|
entry.terminator = buf[offset + 0x23];
|
||||||
entry.fileType = buf[offset + 0x24];
|
entry.fileType = buf[offset + 0x24];
|
||||||
entry.unknown = buf[offset + 0x25];
|
entry.unknown = buf[offset + 0x25];
|
||||||
entry.fileID = BigEndianBitConverter.ToInt16(buf, offset + 0x26);
|
entry.fileID = BigEndianBitConverter.ToInt16(buf, offset + 0x26);
|
||||||
|
|||||||
@@ -88,27 +88,43 @@ namespace DiscImageChef.Filesystems.LisaFS
|
|||||||
file.filenameLen = sector[0];
|
file.filenameLen = sector[0];
|
||||||
file.filename = new byte[file.filenameLen];
|
file.filename = new byte[file.filenameLen];
|
||||||
Array.Copy(sector, 0x01, file.filename, 0, file.filenameLen);
|
Array.Copy(sector, 0x01, file.filename, 0, file.filenameLen);
|
||||||
file.timestamp = BigEndianBitConverter.ToUInt32(sector, 0x20);
|
file.unknown1 = BigEndianBitConverter.ToUInt16(sector, 0x20);
|
||||||
file.unknown1 = new byte[3];
|
file.file_uid = BigEndianBitConverter.ToUInt64(sector, 0x22);
|
||||||
Array.Copy(sector, 0x24, file.unknown1, 0, 3);
|
file.unknown2 = sector[0x2A];
|
||||||
file.serial = new byte[3];
|
file.etype = sector[0x2B];
|
||||||
Array.Copy(sector, 0x27, file.serial, 0, 3);
|
file.ftype = (FileType)sector[0x2C];
|
||||||
file.unknown2 = BigEndianBitConverter.ToUInt32(sector, 0x2A);
|
file.unknown3 = sector[0x2D];
|
||||||
file.dtc = BigEndianBitConverter.ToUInt32(sector, 0x2E);
|
file.dtc = BigEndianBitConverter.ToUInt32(sector, 0x2E);
|
||||||
file.dta = BigEndianBitConverter.ToUInt32(sector, 0x32);
|
file.dta = BigEndianBitConverter.ToUInt32(sector, 0x32);
|
||||||
file.dtm = BigEndianBitConverter.ToUInt32(sector, 0x36);
|
file.dtm = BigEndianBitConverter.ToUInt32(sector, 0x36);
|
||||||
file.dtb = BigEndianBitConverter.ToUInt32(sector, 0x3A);
|
file.dtb = BigEndianBitConverter.ToUInt32(sector, 0x3A);
|
||||||
file.dts = BigEndianBitConverter.ToUInt32(sector, 0x3E);
|
file.dts = BigEndianBitConverter.ToUInt32(sector, 0x3E);
|
||||||
file.unknown3 = new byte[32];
|
file.serial = BigEndianBitConverter.ToUInt32(sector, 0x42);
|
||||||
Array.Copy(sector, 0x42, file.unknown3, 0, 32);
|
file.unknown4 = sector[0x46];
|
||||||
file.flags = sector[0x62];
|
file.locked = sector[0x47];
|
||||||
|
file.protect = sector[0x48];
|
||||||
|
file.master = sector[0x49];
|
||||||
|
file.scavenged = sector[0x4A];
|
||||||
|
file.closed = sector[0x4B];
|
||||||
|
file.open = sector[0x4C];
|
||||||
|
file.unknown5 = new byte[11];
|
||||||
|
Array.Copy(sector, 0x4D, file.unknown5, 0, 11);
|
||||||
|
file.release = BigEndianBitConverter.ToUInt16(sector, 0x58);
|
||||||
|
file.build = BigEndianBitConverter.ToUInt16(sector, 0x5A);
|
||||||
|
file.compatibility = BigEndianBitConverter.ToUInt16(sector, 0x5C);
|
||||||
|
file.revision = BigEndianBitConverter.ToUInt16(sector, 0x5E);
|
||||||
|
file.unknown6 = BigEndianBitConverter.ToUInt16(sector, 0x60);
|
||||||
|
file.password_valid = sector[0x62];
|
||||||
file.password = new byte[8];
|
file.password = new byte[8];
|
||||||
Array.Copy(sector, 0x63, file.password, 0, 8);
|
Array.Copy(sector, 0x63, file.password, 0, 8);
|
||||||
file.unknown4 = new byte[21];
|
file.unknown7 = new byte[3];
|
||||||
Array.Copy(sector, 0x6B, file.unknown4, 0, 21);
|
Array.Copy(sector, 0x6B, file.unknown7, 0, 3);
|
||||||
|
file.overhead = BigEndianBitConverter.ToUInt16(sector, 0x6E);
|
||||||
|
file.unknown8 = new byte[16];
|
||||||
|
Array.Copy(sector, 0x70, file.unknown8, 0, 16);
|
||||||
file.length = BigEndianBitConverter.ToInt32(sector, 0x80);
|
file.length = BigEndianBitConverter.ToInt32(sector, 0x80);
|
||||||
file.unknown5 = BigEndianBitConverter.ToInt32(sector, 0x84);
|
file.unknown9 = BigEndianBitConverter.ToInt32(sector, 0x84);
|
||||||
file.unknown6 = BigEndianBitConverter.ToInt16(sector, 0x17E);
|
file.unknown10 = BigEndianBitConverter.ToInt16(sector, 0x17E);
|
||||||
file.LisaInfo = new byte[128];
|
file.LisaInfo = new byte[128];
|
||||||
Array.Copy(sector, 0x180, file.LisaInfo, 0, 128);
|
Array.Copy(sector, 0x180, file.LisaInfo, 0, 128);
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ using DiscImageChef.ImagePlugins;
|
|||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using DiscImageChef.Console;
|
using DiscImageChef.Console;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
namespace DiscImageChef.Filesystems.LisaFS
|
namespace DiscImageChef.Filesystems.LisaFS
|
||||||
{
|
{
|
||||||
@@ -143,16 +144,44 @@ namespace DiscImageChef.Filesystems.LisaFS
|
|||||||
return error;
|
return error;
|
||||||
|
|
||||||
attributes = new FileAttributes();
|
attributes = new FileAttributes();
|
||||||
// TODO: Subcatalogs
|
|
||||||
attributes = FileAttributes.File;
|
switch(extFile.ftype)
|
||||||
attributes |= FileAttributes.Extents;
|
{
|
||||||
if((extFile.flags & 0x08) == 0x08)
|
case FileType.Spool:
|
||||||
|
attributes |= FileAttributes.CharDevice;
|
||||||
|
break;
|
||||||
|
case FileType.UserCat:
|
||||||
|
case FileType.RootCat:
|
||||||
|
attributes |= FileAttributes.Directory;
|
||||||
|
break;
|
||||||
|
case FileType.Pipe:
|
||||||
|
attributes |= FileAttributes.Pipe;
|
||||||
|
break;
|
||||||
|
case FileType.Undefined:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
attributes |= FileAttributes.File;
|
||||||
|
// Subcatalogs use extents?
|
||||||
|
attributes |= FileAttributes.Extents;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(extFile.protect > 0)
|
||||||
|
attributes |= FileAttributes.Immutable;
|
||||||
|
if(extFile.locked > 0)
|
||||||
|
attributes |= FileAttributes.ReadOnly;
|
||||||
|
if(extFile.password_valid > 0)
|
||||||
attributes |= FileAttributes.Password;
|
attributes |= FileAttributes.Password;
|
||||||
|
|
||||||
return Errno.NoError;
|
return Errno.NoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
Errno ReadSystemFile(Int16 fileId, out byte[] buf)
|
Errno ReadSystemFile(Int16 fileId, out byte[] buf)
|
||||||
|
{
|
||||||
|
return ReadSystemFile(fileId, out buf, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
Errno ReadSystemFile(Int16 fileId, out byte[] buf, bool tags)
|
||||||
{
|
{
|
||||||
buf = null;
|
buf = null;
|
||||||
if(!mounted || !debug)
|
if(!mounted || !debug)
|
||||||
@@ -164,7 +193,7 @@ namespace DiscImageChef.Filesystems.LisaFS
|
|||||||
return Errno.InvalidArgument;
|
return Errno.InvalidArgument;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(systemFileCache.TryGetValue(fileId, out buf))
|
if(systemFileCache.TryGetValue(fileId, out buf) && !tags)
|
||||||
return Errno.NoError;
|
return Errno.NoError;
|
||||||
|
|
||||||
int count = 0;
|
int count = 0;
|
||||||
@@ -182,7 +211,10 @@ namespace DiscImageChef.Filesystems.LisaFS
|
|||||||
if(count == 0)
|
if(count == 0)
|
||||||
return Errno.NoSuchFile;
|
return Errno.NoSuchFile;
|
||||||
|
|
||||||
buf = new byte[count * device.GetSectorSize()];
|
if(!tags)
|
||||||
|
buf = new byte[count * device.GetSectorSize()];
|
||||||
|
else
|
||||||
|
buf = new byte[count * 12];
|
||||||
|
|
||||||
// Should be enough to check 100 sectors?
|
// Should be enough to check 100 sectors?
|
||||||
for(ulong i = 0; i < 100; i++)
|
for(ulong i = 0; i < 100; i++)
|
||||||
@@ -193,12 +225,20 @@ namespace DiscImageChef.Filesystems.LisaFS
|
|||||||
if(id == fileId)
|
if(id == fileId)
|
||||||
{
|
{
|
||||||
UInt16 pos = BigEndianBitConverter.ToUInt16(tag, 0x06);
|
UInt16 pos = BigEndianBitConverter.ToUInt16(tag, 0x06);
|
||||||
byte[] sector = device.ReadSector(i);
|
byte[] sector;
|
||||||
|
|
||||||
|
if(!tags)
|
||||||
|
sector = device.ReadSector(i);
|
||||||
|
else
|
||||||
|
sector = device.ReadSectorTag(i, SectorTagType.AppleSectorTag);
|
||||||
|
|
||||||
Array.Copy(sector, 0, buf, sector.Length * pos, sector.Length);
|
Array.Copy(sector, 0, buf, sector.Length * pos, sector.Length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
systemFileCache.Add(fileId, buf);
|
if(!tags)
|
||||||
|
systemFileCache.Add(fileId, buf);
|
||||||
|
|
||||||
return Errno.NoError;
|
return Errno.NoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,7 +273,6 @@ namespace DiscImageChef.Filesystems.LisaFS
|
|||||||
|
|
||||||
stat.CreationTime = DateHandlers.LisaToDateTime(file.dtc);
|
stat.CreationTime = DateHandlers.LisaToDateTime(file.dtc);
|
||||||
stat.AccessTime = DateHandlers.LisaToDateTime(file.dta);
|
stat.AccessTime = DateHandlers.LisaToDateTime(file.dta);
|
||||||
stat.StatusChangeTime = DateHandlers.LisaToDateTime(file.timestamp);
|
|
||||||
stat.BackupTime = DateHandlers.LisaToDateTime(file.dtb);
|
stat.BackupTime = DateHandlers.LisaToDateTime(file.dtb);
|
||||||
stat.LastWriteTime = DateHandlers.LisaToDateTime(file.dtm);
|
stat.LastWriteTime = DateHandlers.LisaToDateTime(file.dtm);
|
||||||
|
|
||||||
@@ -294,7 +333,6 @@ namespace DiscImageChef.Filesystems.LisaFS
|
|||||||
|
|
||||||
stat.CreationTime = DateHandlers.LisaToDateTime(file.dtc);
|
stat.CreationTime = DateHandlers.LisaToDateTime(file.dtc);
|
||||||
stat.AccessTime = DateHandlers.LisaToDateTime(file.dta);
|
stat.AccessTime = DateHandlers.LisaToDateTime(file.dta);
|
||||||
stat.StatusChangeTime = DateHandlers.LisaToDateTime(file.timestamp);
|
|
||||||
stat.BackupTime = DateHandlers.LisaToDateTime(file.dtb);
|
stat.BackupTime = DateHandlers.LisaToDateTime(file.dtb);
|
||||||
stat.LastWriteTime = DateHandlers.LisaToDateTime(file.dtm);
|
stat.LastWriteTime = DateHandlers.LisaToDateTime(file.dtm);
|
||||||
|
|
||||||
@@ -316,15 +354,22 @@ namespace DiscImageChef.Filesystems.LisaFS
|
|||||||
}
|
}
|
||||||
|
|
||||||
Errno ReadFile(Int16 fileId, out byte[] buf)
|
Errno ReadFile(Int16 fileId, out byte[] buf)
|
||||||
|
{
|
||||||
|
return ReadFile(fileId, out buf, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
Errno ReadFile(Int16 fileId, out byte[] buf, bool tags)
|
||||||
{
|
{
|
||||||
buf = null;
|
buf = null;
|
||||||
if(!mounted)
|
if(!mounted)
|
||||||
return Errno.AccessDenied;
|
return Errno.AccessDenied;
|
||||||
|
|
||||||
|
tags &= debug;
|
||||||
|
|
||||||
if(fileId <= 4)
|
if(fileId <= 4)
|
||||||
return Errno.InvalidArgument;
|
return Errno.InvalidArgument;
|
||||||
|
|
||||||
if(fileCache.TryGetValue(fileId, out buf))
|
if(!tags && fileCache.TryGetValue(fileId, out buf))
|
||||||
return Errno.NoError;
|
return Errno.NoError;
|
||||||
|
|
||||||
Errno error;
|
Errno error;
|
||||||
@@ -334,27 +379,44 @@ namespace DiscImageChef.Filesystems.LisaFS
|
|||||||
if(error != Errno.NoError)
|
if(error != Errno.NoError)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
byte[] temp = new byte[file.length * (int)device.GetSectors()];
|
int sectorSize;
|
||||||
|
if(tags)
|
||||||
|
sectorSize = 12;
|
||||||
|
else
|
||||||
|
sectorSize = (int)device.GetSectorSize();
|
||||||
|
|
||||||
|
byte[] temp = new byte[file.length * sectorSize];
|
||||||
|
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
for(int i = 0; i < file.extents.Length; i++)
|
for(int i = 0; i < file.extents.Length; i++)
|
||||||
{
|
{
|
||||||
byte[] sector = device.ReadSectors((ulong)(file.extents[i].start + mddf.mddf_block), (uint)file.extents[i].length);
|
byte[] sector;
|
||||||
|
|
||||||
|
if(!tags)
|
||||||
|
sector = device.ReadSectors((ulong)(file.extents[i].start + mddf.mddf_block), (uint)file.extents[i].length);
|
||||||
|
else
|
||||||
|
sector = device.ReadSectorsTag((ulong)(file.extents[i].start + mddf.mddf_block), (uint)file.extents[i].length, SectorTagType.AppleSectorTag);
|
||||||
|
|
||||||
Array.Copy(sector, 0, temp, offset, sector.Length);
|
Array.Copy(sector, 0, temp, offset, sector.Length);
|
||||||
offset += sector.Length;
|
offset += sector.Length;
|
||||||
}
|
}
|
||||||
|
|
||||||
int realSize;
|
if(!tags)
|
||||||
if(fileSizeCache.TryGetValue(fileId, out realSize))
|
|
||||||
{
|
{
|
||||||
buf = new byte[realSize];
|
int realSize;
|
||||||
Array.Copy(temp, 0, buf, 0, realSize);
|
if(fileSizeCache.TryGetValue(fileId, out realSize))
|
||||||
|
{
|
||||||
|
buf = new byte[realSize];
|
||||||
|
Array.Copy(temp, 0, buf, 0, realSize);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
buf = temp;
|
||||||
|
|
||||||
|
fileCache.Add(fileId, buf);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
buf = temp;
|
buf = temp;
|
||||||
|
|
||||||
fileCache.Add(fileId, buf);
|
|
||||||
return Errno.NoError;
|
return Errno.NoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -229,8 +229,8 @@ namespace DiscImageChef.Filesystems.LisaFS
|
|||||||
public ushort zero;
|
public ushort zero;
|
||||||
/// <summary>0x03, filename, 32-bytes, null-padded</summary>
|
/// <summary>0x03, filename, 32-bytes, null-padded</summary>
|
||||||
public byte[] filename;
|
public byte[] filename;
|
||||||
/// <summary>0x23, seems to be always zero</summary>
|
/// <summary>0x23, null-termination</summary>
|
||||||
public byte padding;
|
public byte terminator;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// At 0x24
|
/// At 0x24
|
||||||
/// 0x03 here for entries 64 bytes long
|
/// 0x03 here for entries 64 bytes long
|
||||||
@@ -267,14 +267,18 @@ namespace DiscImageChef.Filesystems.LisaFS
|
|||||||
public byte filenameLen;
|
public byte filenameLen;
|
||||||
/// <summary>0x01, filename</summary>
|
/// <summary>0x01, filename</summary>
|
||||||
public byte[] filename;
|
public byte[] filename;
|
||||||
/// <summary>0x20, unknown timestamp</summary>
|
/// <summary>0x20, unknown</summary>
|
||||||
public UInt32 timestamp;
|
public ushort unknown1;
|
||||||
/// <summary>0x24, 3 bytes, unknown</summary>
|
/// <summary>0x22, 8 bytes</summary>
|
||||||
public byte[] unknown1;
|
public UInt64 file_uid;
|
||||||
/// <summary>0x27, 3 bytes, machine serial number</summary>
|
/// <summary>0x2A, unknown</summary>
|
||||||
public byte[] serial;
|
public byte unknown2;
|
||||||
/// <summary>0x2A, 4 bytes, unknown</summary>
|
/// <summary>0x2B, entry type? gets modified</summary>
|
||||||
public uint unknown2;
|
public byte etype;
|
||||||
|
/// <summary>0x2C, file type</summary>
|
||||||
|
public FileType ftype;
|
||||||
|
/// <summary>0x2D, unknown</summary>
|
||||||
|
public byte unknown3;
|
||||||
/// <summary>0x2E, creation time</summary>
|
/// <summary>0x2E, creation time</summary>
|
||||||
public UInt32 dtc;
|
public UInt32 dtc;
|
||||||
/// <summary>0x32, last access time</summary>
|
/// <summary>0x32, last access time</summary>
|
||||||
@@ -285,22 +289,52 @@ namespace DiscImageChef.Filesystems.LisaFS
|
|||||||
public UInt32 dtb;
|
public UInt32 dtb;
|
||||||
/// <summary>0x3E, scavenge time</summary>
|
/// <summary>0x3E, scavenge time</summary>
|
||||||
public UInt32 dts;
|
public UInt32 dts;
|
||||||
/// <summary>0x42, unknown, 32 bytes</summary>
|
/// <summary>0x42, machine serial number</summary>
|
||||||
public byte[] unknown3;
|
public UInt32 serial;
|
||||||
/// <summary>0x62, flags?, 0x08 set if password is valid</summary>
|
/// <summary>0x46, unknown</summary>
|
||||||
public byte flags;
|
public byte unknown4;
|
||||||
|
/// <summary>0x47, locked file</summary>
|
||||||
|
public byte locked;
|
||||||
|
/// <summary>0x48, protected file</summary>
|
||||||
|
public byte protect;
|
||||||
|
/// <summary>0x49, master file</summary>
|
||||||
|
public byte master;
|
||||||
|
/// <summary>0x4A, scavenged file</summary>
|
||||||
|
public byte scavenged;
|
||||||
|
/// <summary>0x4B, file closed by os</summary>
|
||||||
|
public byte closed;
|
||||||
|
/// <summary>0x4C, file left open</summary>
|
||||||
|
public byte open;
|
||||||
|
/// <summary>0x4D, 11 bytes, unknown</summary>
|
||||||
|
public byte[] unknown5;
|
||||||
|
/// <summary>0x58, Release number</summary>
|
||||||
|
public UInt16 release;
|
||||||
|
/// <summary>0x5A, Build number</summary>
|
||||||
|
public UInt16 build;
|
||||||
|
/// <summary>0x5C, Compatibility level</summary>
|
||||||
|
public UInt16 compatibility;
|
||||||
|
/// <summary>0x5E, Revision level</summary>
|
||||||
|
public UInt16 revision;
|
||||||
|
/// <summary>0x60, unknown</summary>
|
||||||
|
public ushort unknown6;
|
||||||
|
/// <summary>0x62, 0x08 set if password is valid</summary>
|
||||||
|
public byte password_valid;
|
||||||
/// <summary>0x63, 8 bytes, scrambled password</summary>
|
/// <summary>0x63, 8 bytes, scrambled password</summary>
|
||||||
public byte[] password;
|
public byte[] password;
|
||||||
/// <summary>0x6B, 21 bytes, unknown</summary>
|
/// <summary>0x6B, 3 bytes, unknown</summary>
|
||||||
public byte[] unknown4;
|
public byte[] unknown7;
|
||||||
|
/// <summary>0x6E, filesystem overhead</summary>
|
||||||
|
public ushort overhead;
|
||||||
|
/// <summary>0x70, 16 bytes, unknown</summary>
|
||||||
|
public byte[] unknown8;
|
||||||
/// <summary>0x80, file length in blocks</summary>
|
/// <summary>0x80, file length in blocks</summary>
|
||||||
public Int32 length;
|
public Int32 length;
|
||||||
/// <summary>0x84, unknown</summary>
|
/// <summary>0x84, unknown</summary>
|
||||||
public Int32 unknown5;
|
public Int32 unknown9;
|
||||||
/// <summary>0x88, extents, can contain up to 41 extents, dunno LisaOS maximum (never seen more than 3)</summary>
|
/// <summary>0x88, extents, can contain up to 41 extents, dunno LisaOS maximum (never seen more than 3)</summary>
|
||||||
public Extent[] extents;
|
public Extent[] extents;
|
||||||
/// <summary>0x17E, unknown, empty, padding?</summary>
|
/// <summary>0x17E, unknown, empty, padding?</summary>
|
||||||
public short unknown6;
|
public short unknown10;
|
||||||
/// <summary>0x180, 128 bytes</summary>
|
/// <summary>0x180, 128 bytes</summary>
|
||||||
public byte[] LisaInfo;
|
public byte[] LisaInfo;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,6 +38,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Configuration;
|
||||||
|
|
||||||
namespace DiscImageChef.Filesystems.LisaFS
|
namespace DiscImageChef.Filesystems.LisaFS
|
||||||
{
|
{
|
||||||
@@ -79,29 +80,40 @@ namespace DiscImageChef.Filesystems.LisaFS
|
|||||||
|
|
||||||
if(fileId == FILEID_MDDF)
|
if(fileId == FILEID_MDDF)
|
||||||
xattrs.Add("com.apple.lisa.password");
|
xattrs.Add("com.apple.lisa.password");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
return Errno.NoError;
|
ExtentFile file;
|
||||||
|
|
||||||
|
Errno error = ReadExtentsFile(fileId, out file);
|
||||||
|
|
||||||
|
if(error != Errno.NoError)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
xattrs = new List<string>();
|
||||||
|
if(file.password_valid > 0)
|
||||||
|
xattrs.Add("com.apple.lisa.password");
|
||||||
|
xattrs.Add("com.apple.lisa.serial");
|
||||||
|
|
||||||
|
if(!ArrayHelpers.ArrayIsNullOrEmpty(file.LisaInfo))
|
||||||
|
xattrs.Add("com.apple.lisa.label");
|
||||||
}
|
}
|
||||||
|
|
||||||
ExtentFile file;
|
if(debug)
|
||||||
|
xattrs.Add("com.apple.lisa.tags");
|
||||||
|
|
||||||
Errno error = ReadExtentsFile(fileId, out file);
|
xattrs.Sort();
|
||||||
|
|
||||||
if(error != Errno.NoError)
|
|
||||||
return error;
|
|
||||||
|
|
||||||
xattrs = new List<string>();
|
|
||||||
if((file.flags & 0x08) == 0x08)
|
|
||||||
xattrs.Add("com.apple.lisa.password");
|
|
||||||
xattrs.Add("com.apple.lisa.serial");
|
|
||||||
|
|
||||||
if(!ArrayHelpers.ArrayIsNullOrEmpty(file.LisaInfo))
|
|
||||||
xattrs.Add("com.apple.lisa.label");
|
|
||||||
|
|
||||||
return Errno.NoError;
|
return Errno.NoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
Errno GetXattr(Int16 fileId, string xattr, out byte[] buf)
|
Errno GetXattr(Int16 fileId, string xattr, out byte[] buf)
|
||||||
|
{
|
||||||
|
return GetXattr(fileId, xattr, out buf, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
Errno GetXattr(Int16 fileId, string xattr, out byte[] buf, bool tags)
|
||||||
{
|
{
|
||||||
buf = null;
|
buf = null;
|
||||||
|
|
||||||
@@ -122,6 +134,9 @@ namespace DiscImageChef.Filesystems.LisaFS
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(debug && xattr == "com.apple.lisa.tags")
|
||||||
|
return ReadSystemFile(fileId, out buf, true);
|
||||||
|
|
||||||
return Errno.NoSuchExtendedAttribute;
|
return Errno.NoSuchExtendedAttribute;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,7 +147,7 @@ namespace DiscImageChef.Filesystems.LisaFS
|
|||||||
if(error != Errno.NoError)
|
if(error != Errno.NoError)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
if(xattr == "com.apple.lisa.password" && (file.flags & 0x08) == 0x08)
|
if(xattr == "com.apple.lisa.password" && file.password_valid > 0)
|
||||||
{
|
{
|
||||||
buf = new byte[8];
|
buf = new byte[8];
|
||||||
Array.Copy(file.password, 0, buf, 0, 8);
|
Array.Copy(file.password, 0, buf, 0, 8);
|
||||||
@@ -141,8 +156,7 @@ namespace DiscImageChef.Filesystems.LisaFS
|
|||||||
|
|
||||||
if(xattr == "com.apple.lisa.serial")
|
if(xattr == "com.apple.lisa.serial")
|
||||||
{
|
{
|
||||||
buf = new byte[3];
|
buf = Encoding.ASCII.GetBytes(file.serial.ToString());
|
||||||
Array.Copy(file.serial, 0, buf, 0, 3);
|
|
||||||
return Errno.NoError;
|
return Errno.NoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,6 +167,9 @@ namespace DiscImageChef.Filesystems.LisaFS
|
|||||||
return Errno.NoError;
|
return Errno.NoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(debug && xattr == "com.apple.lisa.tags")
|
||||||
|
return ReadFile(fileId, out buf, true);
|
||||||
|
|
||||||
return Errno.NoSuchExtendedAttribute;
|
return Errno.NoSuchExtendedAttribute;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -142,7 +142,9 @@ namespace DiscImageChef.Filesystems
|
|||||||
/// <summary>Subdirectories inside of this directory are not related and should be allocated elsewhere</summary>
|
/// <summary>Subdirectories inside of this directory are not related and should be allocated elsewhere</summary>
|
||||||
TopDirectory = 0x400000000000,
|
TopDirectory = 0x400000000000,
|
||||||
/// <summary>If file is deleted, contents should be stored, for a possible future undeletion</summary>
|
/// <summary>If file is deleted, contents should be stored, for a possible future undeletion</summary>
|
||||||
Undeletable = 0x800000000000
|
Undeletable = 0x800000000000,
|
||||||
|
/// <summary>File is a pipe</summary>
|
||||||
|
Pipe = 0x1000000000000
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
394
DiscImageChef/Commands/ExtractFiles.cs
Normal file
394
DiscImageChef/Commands/ExtractFiles.cs
Normal file
@@ -0,0 +1,394 @@
|
|||||||
|
// /***************************************************************************
|
||||||
|
// The Disc Image Chef
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Filename : ExtractFiles.cs
|
||||||
|
// Version : 1.0
|
||||||
|
// Author(s) : Natalia Portillo
|
||||||
|
//
|
||||||
|
// Component : Component
|
||||||
|
//
|
||||||
|
// Revision : $Revision$
|
||||||
|
// Last change by : $Author$
|
||||||
|
// Date : $Date$
|
||||||
|
//
|
||||||
|
// --[ Description ] ----------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Description
|
||||||
|
//
|
||||||
|
// --[ License ] --------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// 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/>.
|
||||||
|
//
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Copyright (C) 2011-2015 Claunia.com
|
||||||
|
// ****************************************************************************/
|
||||||
|
// //$Id$
|
||||||
|
|
||||||
|
// //$Id$
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using DiscImageChef.Console;
|
||||||
|
using DiscImageChef.Filesystems;
|
||||||
|
using DiscImageChef.ImagePlugins;
|
||||||
|
using DiscImageChef.PartPlugins;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace DiscImageChef.Commands
|
||||||
|
{
|
||||||
|
public class ExtractFiles
|
||||||
|
{
|
||||||
|
public static void doExtractFiles(ExtractFilesOptions options)
|
||||||
|
{
|
||||||
|
DicConsole.DebugWriteLine("Extract-Files command", "--debug={0}", options.Debug);
|
||||||
|
DicConsole.DebugWriteLine("Extract-Files command", "--verbose={0}", options.Verbose);
|
||||||
|
DicConsole.DebugWriteLine("Extract-Files command", "--input={0}", options.InputFile);
|
||||||
|
DicConsole.DebugWriteLine("Extract-Files command", "--xattrs={0}", options.Xattrs);
|
||||||
|
DicConsole.DebugWriteLine("Extract-Files command", "--output={0}", options.OutputDir);
|
||||||
|
|
||||||
|
if(!System.IO.File.Exists(options.InputFile))
|
||||||
|
{
|
||||||
|
DicConsole.ErrorWriteLine("Specified file does not exist.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PluginBase plugins = new PluginBase();
|
||||||
|
plugins.RegisterAllPlugins();
|
||||||
|
|
||||||
|
List<string> id_plugins;
|
||||||
|
Filesystem _plugin;
|
||||||
|
ImagePlugin _imageFormat;
|
||||||
|
Errno error;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_imageFormat = ImageFormat.Detect(options.InputFile);
|
||||||
|
|
||||||
|
if(_imageFormat == null)
|
||||||
|
{
|
||||||
|
DicConsole.WriteLine("Image format not identified, not proceeding with analysis.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(options.Verbose)
|
||||||
|
DicConsole.VerboseWriteLine("Image format identified by {0} ({1}).", _imageFormat.Name, _imageFormat.PluginUUID);
|
||||||
|
else
|
||||||
|
DicConsole.WriteLine("Image format identified by {0}.", _imageFormat.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Directory.Exists(options.OutputDir) || File.Exists(options.OutputDir))
|
||||||
|
{
|
||||||
|
DicConsole.ErrorWriteLine("Destination exists, aborting.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Directory.CreateDirectory(options.OutputDir);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if(!_imageFormat.OpenImage(options.InputFile))
|
||||||
|
{
|
||||||
|
DicConsole.WriteLine("Unable to open image format");
|
||||||
|
DicConsole.WriteLine("No error given");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DicConsole.DebugWriteLine("Extract-Files command", "Correctly opened image file.");
|
||||||
|
DicConsole.DebugWriteLine("Extract-Files command", "Image without headers is {0} bytes.", _imageFormat.GetImageSize());
|
||||||
|
DicConsole.DebugWriteLine("Extract-Files command", "Image has {0} sectors.", _imageFormat.GetSectors());
|
||||||
|
DicConsole.DebugWriteLine("Extract-Files command", "Image identifies disk type as {0}.", _imageFormat.GetMediaType());
|
||||||
|
|
||||||
|
Core.Statistics.AddMediaFormat(_imageFormat.GetImageFormat());
|
||||||
|
Core.Statistics.AddMedia(_imageFormat.ImageInfo.mediaType, false);
|
||||||
|
}
|
||||||
|
catch(Exception ex)
|
||||||
|
{
|
||||||
|
DicConsole.ErrorWriteLine("Unable to open image format");
|
||||||
|
DicConsole.ErrorWriteLine("Error: {0}", ex.Message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<CommonTypes.Partition> partitions = new List<CommonTypes.Partition>();
|
||||||
|
string partition_scheme = "";
|
||||||
|
|
||||||
|
// TODO: Solve possibility of multiple partition schemes (CUE + MBR, MBR + RDB, CUE + APM, etc)
|
||||||
|
foreach(PartPlugin _partplugin in plugins.PartPluginsList.Values)
|
||||||
|
{
|
||||||
|
List<CommonTypes.Partition> _partitions;
|
||||||
|
if(_partplugin.GetInformation(_imageFormat, out _partitions))
|
||||||
|
{
|
||||||
|
partition_scheme = _partplugin.Name;
|
||||||
|
partitions.AddRange(_partitions);
|
||||||
|
Core.Statistics.AddPartition(_partplugin.Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(_imageFormat.ImageHasPartitions())
|
||||||
|
{
|
||||||
|
partition_scheme = _imageFormat.GetImageFormat();
|
||||||
|
partitions.AddRange(_imageFormat.GetPartitions());
|
||||||
|
}
|
||||||
|
|
||||||
|
if(partition_scheme == "")
|
||||||
|
DicConsole.DebugWriteLine("Extract-Files command", "No partitions found");
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DicConsole.WriteLine("Partition scheme identified as {0}", partition_scheme);
|
||||||
|
DicConsole.WriteLine("{0} partitions found.", partitions.Count);
|
||||||
|
|
||||||
|
for(int i = 0; i < partitions.Count; i++)
|
||||||
|
{
|
||||||
|
DicConsole.WriteLine();
|
||||||
|
DicConsole.WriteLine("Partition {0}:", partitions[i].PartitionSequence);
|
||||||
|
|
||||||
|
DicConsole.WriteLine("Identifying filesystem on partition");
|
||||||
|
|
||||||
|
IdentifyFilesystems(_imageFormat, out id_plugins, partitions[i].PartitionStartSector, partitions[i].PartitionStartSector + partitions[i].PartitionSectors);
|
||||||
|
if(id_plugins.Count == 0)
|
||||||
|
DicConsole.WriteLine("Filesystem not identified");
|
||||||
|
else if(id_plugins.Count > 1)
|
||||||
|
{
|
||||||
|
DicConsole.WriteLine(String.Format("Identified by {0} plugins", id_plugins.Count));
|
||||||
|
|
||||||
|
foreach(string plugin_name in id_plugins)
|
||||||
|
{
|
||||||
|
if(plugins.PluginsList.TryGetValue(plugin_name, out _plugin))
|
||||||
|
{
|
||||||
|
DicConsole.WriteLine(String.Format("As identified by {0}.", _plugin.Name));
|
||||||
|
Filesystem fs = (Filesystem)_plugin.GetType().GetConstructor(new Type[] { typeof(ImagePlugin), typeof(ulong), typeof(ulong) }).Invoke(new object[] { _imageFormat, partitions[i].PartitionStartSector, partitions[i].PartitionStartSector + partitions[i].PartitionSectors });
|
||||||
|
|
||||||
|
error = fs.Mount(options.Debug);
|
||||||
|
if(error == Errno.NoError)
|
||||||
|
{
|
||||||
|
List<string> rootDir = new List<string>();
|
||||||
|
error = fs.ReadDir("/", ref rootDir);
|
||||||
|
if(error == Errno.NoError)
|
||||||
|
{
|
||||||
|
foreach(string entry in rootDir)
|
||||||
|
DicConsole.WriteLine("{0}", entry);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
DicConsole.ErrorWriteLine("Error {0} reading root directory {0}", error.ToString());
|
||||||
|
|
||||||
|
Core.Statistics.AddFilesystem(fs.XmlFSType.Type);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
DicConsole.ErrorWriteLine("Unable to mount device, error {0}", error.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
plugins.PluginsList.TryGetValue(id_plugins[0], out _plugin);
|
||||||
|
DicConsole.WriteLine(String.Format("Identified by {0}.", _plugin.Name));
|
||||||
|
Filesystem fs = (Filesystem)_plugin.GetType().GetConstructor(new Type[] { typeof(ImagePlugin), typeof(ulong), typeof(ulong) }).Invoke(new object[] { _imageFormat, partitions[i].PartitionStartSector, partitions[i].PartitionStartSector + partitions[i].PartitionSectors });
|
||||||
|
error = fs.Mount(options.Debug);
|
||||||
|
if(error == Errno.NoError)
|
||||||
|
{
|
||||||
|
List<string> rootDir = new List<string>();
|
||||||
|
error = fs.ReadDir("/", ref rootDir);
|
||||||
|
if(error == Errno.NoError)
|
||||||
|
{
|
||||||
|
foreach(string entry in rootDir)
|
||||||
|
DicConsole.WriteLine("{0}", entry);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
DicConsole.ErrorWriteLine("Error {0} reading root directory {0}", error.ToString());
|
||||||
|
|
||||||
|
Core.Statistics.AddFilesystem(fs.XmlFSType.Type);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
DicConsole.ErrorWriteLine("Unable to mount device, error {0}", error.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IdentifyFilesystems(_imageFormat, out id_plugins, 0, _imageFormat.GetSectors() - 1);
|
||||||
|
if(id_plugins.Count == 0)
|
||||||
|
DicConsole.WriteLine("Filesystem not identified");
|
||||||
|
else if(id_plugins.Count > 1)
|
||||||
|
{
|
||||||
|
DicConsole.WriteLine(String.Format("Identified by {0} plugins", id_plugins.Count));
|
||||||
|
|
||||||
|
foreach(string plugin_name in id_plugins)
|
||||||
|
{
|
||||||
|
if(plugins.PluginsList.TryGetValue(plugin_name, out _plugin))
|
||||||
|
{
|
||||||
|
DicConsole.WriteLine(String.Format("As identified by {0}.", _plugin.Name));
|
||||||
|
Filesystem fs = (Filesystem)_plugin.GetType().GetConstructor(new Type[] { typeof(ImagePlugin), typeof(ulong), typeof(ulong) }).Invoke(new object[] { _imageFormat, (ulong)0, (ulong)(_imageFormat.GetSectors() - 1) });
|
||||||
|
error = fs.Mount(options.Debug);
|
||||||
|
if(error == Errno.NoError)
|
||||||
|
{
|
||||||
|
List<string> rootDir = new List<string>();
|
||||||
|
error = fs.ReadDir("/", ref rootDir);
|
||||||
|
if(error == Errno.NoError)
|
||||||
|
{
|
||||||
|
foreach(string entry in rootDir)
|
||||||
|
DicConsole.WriteLine("{0}", entry);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
DicConsole.ErrorWriteLine("Error {0} reading root directory {0}", error.ToString());
|
||||||
|
|
||||||
|
Core.Statistics.AddFilesystem(fs.XmlFSType.Type);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
DicConsole.ErrorWriteLine("Unable to mount device, error {0}", error.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
plugins.PluginsList.TryGetValue(id_plugins[0], out _plugin);
|
||||||
|
DicConsole.WriteLine(String.Format("Identified by {0}.", _plugin.Name));
|
||||||
|
Filesystem fs = (Filesystem)_plugin.GetType().GetConstructor(new Type[] { typeof(ImagePlugin), typeof(ulong), typeof(ulong) }).Invoke(new object[] { _imageFormat, (ulong)0, (ulong)(_imageFormat.GetSectors() - 1) });
|
||||||
|
error = fs.Mount(options.Debug);
|
||||||
|
if(error == Errno.NoError)
|
||||||
|
{
|
||||||
|
List<string> rootDir = new List<string>();
|
||||||
|
error = fs.ReadDir("/", ref rootDir);
|
||||||
|
if(error == Errno.NoError)
|
||||||
|
{
|
||||||
|
foreach(string entry in rootDir)
|
||||||
|
{
|
||||||
|
FileEntryInfo stat = new FileEntryInfo();
|
||||||
|
string outputPath;
|
||||||
|
FileStream outputFile;
|
||||||
|
|
||||||
|
error = fs.Stat(entry, ref stat);
|
||||||
|
if(error == Errno.NoError)
|
||||||
|
{
|
||||||
|
if(options.Xattrs)
|
||||||
|
{
|
||||||
|
List<string> xattrs = new List<string>();
|
||||||
|
|
||||||
|
error = fs.ListXAttr(entry, ref xattrs);
|
||||||
|
if(error == Errno.NoError)
|
||||||
|
{
|
||||||
|
foreach(string xattr in xattrs)
|
||||||
|
{
|
||||||
|
byte[] xattrBuf = new byte[0];
|
||||||
|
error = fs.GetXattr(entry, xattr, ref xattrBuf);
|
||||||
|
if(error == Errno.NoError)
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(Path.Combine(options.OutputDir,
|
||||||
|
fs.XmlFSType.Type,
|
||||||
|
fs.XmlFSType.VolumeName,
|
||||||
|
".xattrs",
|
||||||
|
xattr));
|
||||||
|
|
||||||
|
outputPath = Path.Combine(options.OutputDir,
|
||||||
|
fs.XmlFSType.Type,
|
||||||
|
fs.XmlFSType.VolumeName,
|
||||||
|
".xattrs",
|
||||||
|
xattr,
|
||||||
|
entry);
|
||||||
|
|
||||||
|
if(!File.Exists(outputPath))
|
||||||
|
{
|
||||||
|
outputFile = new FileStream(outputPath, FileMode.CreateNew, FileAccess.ReadWrite,
|
||||||
|
FileShare.None);
|
||||||
|
outputFile.Write(xattrBuf, 0, xattrBuf.Length);
|
||||||
|
outputFile.Close();
|
||||||
|
System.IO.FileInfo fi = new System.IO.FileInfo(outputPath);
|
||||||
|
#pragma warning disable RECS0022 // A catch clause that catches System.Exception and has an empty body
|
||||||
|
try { fi.CreationTimeUtc = stat.CreationTimeUtc; } catch { }
|
||||||
|
try { fi.LastWriteTimeUtc = stat.LastWriteTimeUtc; } catch { }
|
||||||
|
try { fi.LastAccessTimeUtc = stat.AccessTimeUtc; } catch { }
|
||||||
|
#pragma warning restore RECS0022 // A catch clause that catches System.Exception and has an empty body
|
||||||
|
DicConsole.WriteLine("Written {0} bytes of xattr {1} from file {2} to {3}",
|
||||||
|
xattrBuf.Length, xattr, entry, outputPath);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
DicConsole.ErrorWriteLine("Cannot write xattr {0} for {1}, output exists",
|
||||||
|
xattr, entry);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Directory.CreateDirectory(Path.Combine(options.OutputDir,
|
||||||
|
fs.XmlFSType.Type,
|
||||||
|
fs.XmlFSType.VolumeName));
|
||||||
|
|
||||||
|
outputPath = Path.Combine(options.OutputDir,
|
||||||
|
fs.XmlFSType.Type,
|
||||||
|
fs.XmlFSType.VolumeName,
|
||||||
|
entry);
|
||||||
|
|
||||||
|
if(!File.Exists(outputPath))
|
||||||
|
{
|
||||||
|
byte[] outBuf = new byte[0];
|
||||||
|
|
||||||
|
error = fs.Read(entry, 0, stat.Length, ref outBuf);
|
||||||
|
|
||||||
|
if(error == Errno.NoError)
|
||||||
|
{
|
||||||
|
outputFile = new FileStream(outputPath, FileMode.CreateNew, FileAccess.ReadWrite,
|
||||||
|
FileShare.None);
|
||||||
|
outputFile.Write(outBuf, 0, outBuf.Length);
|
||||||
|
outputFile.Close();
|
||||||
|
System.IO.FileInfo fi = new System.IO.FileInfo(outputPath);
|
||||||
|
#pragma warning disable RECS0022 // A catch clause that catches System.Exception and has an empty body
|
||||||
|
try { fi.CreationTimeUtc = stat.CreationTimeUtc; } catch { }
|
||||||
|
try { fi.LastWriteTimeUtc = stat.LastWriteTimeUtc; } catch { }
|
||||||
|
try { fi.LastAccessTimeUtc = stat.AccessTimeUtc; } catch { }
|
||||||
|
#pragma warning restore RECS0022 // A catch clause that catches System.Exception and has an empty body
|
||||||
|
DicConsole.WriteLine("Written {0} bytes of file {1} to {2}",
|
||||||
|
outBuf.Length, entry, outputPath);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
DicConsole.ErrorWriteLine("Error {0} reading file {1}", error, entry);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
DicConsole.ErrorWriteLine("Cannot write file {1}, output exists", entry);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
DicConsole.ErrorWriteLine("Error reading file {0}", entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
DicConsole.ErrorWriteLine("Error {0} reading root directory {0}", error.ToString());
|
||||||
|
|
||||||
|
Core.Statistics.AddFilesystem(fs.XmlFSType.Type);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
DicConsole.ErrorWriteLine("Unable to mount device, error {0}", error.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(Exception ex)
|
||||||
|
{
|
||||||
|
DicConsole.ErrorWriteLine(String.Format("Error reading file: {0}", ex.Message));
|
||||||
|
DicConsole.DebugWriteLine("Extract-Files command", ex.StackTrace);
|
||||||
|
}
|
||||||
|
|
||||||
|
Core.Statistics.AddCommand("ls");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void IdentifyFilesystems(ImagePlugin imagePlugin, out List<string> id_plugins, ulong partitionStart, ulong partitionEnd)
|
||||||
|
{
|
||||||
|
id_plugins = new List<string>();
|
||||||
|
PluginBase plugins = new PluginBase();
|
||||||
|
plugins.RegisterAllPlugins();
|
||||||
|
|
||||||
|
foreach(Filesystem _plugin in plugins.PluginsList.Values)
|
||||||
|
{
|
||||||
|
if(_plugin.Identify(imagePlugin, partitionStart, partitionEnd))
|
||||||
|
id_plugins.Add(_plugin.Name.ToLower());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -67,6 +67,7 @@
|
|||||||
<Compile Include="Core\IBGLog.cs" />
|
<Compile Include="Core\IBGLog.cs" />
|
||||||
<Compile Include="Core\MHDDLog.cs" />
|
<Compile Include="Core\MHDDLog.cs" />
|
||||||
<Compile Include="Commands\Ls.cs" />
|
<Compile Include="Commands\Ls.cs" />
|
||||||
|
<Compile Include="Commands\ExtractFiles.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ namespace DiscImageChef
|
|||||||
|
|
||||||
Parser.Default.ParseArguments<AnalyzeOptions, CompareOptions, ChecksumOptions, EntropyOptions, VerifyOptions, PrintHexOptions,
|
Parser.Default.ParseArguments<AnalyzeOptions, CompareOptions, ChecksumOptions, EntropyOptions, VerifyOptions, PrintHexOptions,
|
||||||
DecodeOptions, DeviceInfoOptions, MediaInfoOptions, MediaScanOptions, FormatsOptions, BenchmarkOptions, CreateSidecarOptions,
|
DecodeOptions, DeviceInfoOptions, MediaInfoOptions, MediaScanOptions, FormatsOptions, BenchmarkOptions, CreateSidecarOptions,
|
||||||
DumpMediaOptions, DeviceReportOptions, ConfigureOptions, StatsOptions, LsOptions>(args)
|
DumpMediaOptions, DeviceReportOptions, ConfigureOptions, StatsOptions, LsOptions, ExtractFilesOptions>(args)
|
||||||
.WithParsed<AnalyzeOptions>(opts =>
|
.WithParsed<AnalyzeOptions>(opts =>
|
||||||
{
|
{
|
||||||
if(opts.Debug)
|
if(opts.Debug)
|
||||||
@@ -216,6 +216,16 @@ namespace DiscImageChef
|
|||||||
Commands.Ls.doLs(opts);
|
Commands.Ls.doLs(opts);
|
||||||
})
|
})
|
||||||
|
|
||||||
|
.WithParsed<ExtractFilesOptions>(opts =>
|
||||||
|
{
|
||||||
|
if(opts.Debug)
|
||||||
|
DicConsole.DebugWriteLineEvent += System.Console.Error.WriteLine;
|
||||||
|
if(opts.Verbose)
|
||||||
|
DicConsole.VerboseWriteLineEvent += System.Console.WriteLine;
|
||||||
|
PrintCopyright();
|
||||||
|
Commands.ExtractFiles.doExtractFiles(opts);
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
.WithParsed<ConfigureOptions>(opts => { PrintCopyright(); Commands.Configure.doConfigure(); })
|
.WithParsed<ConfigureOptions>(opts => { PrintCopyright(); Commands.Configure.doConfigure(); })
|
||||||
.WithParsed<StatsOptions>(opts => { PrintCopyright(); Commands.Statistics.showStats(); })
|
.WithParsed<StatsOptions>(opts => { PrintCopyright(); Commands.Statistics.showStats(); })
|
||||||
|
|||||||
@@ -332,5 +332,19 @@ namespace DiscImageChef
|
|||||||
[Option('l', "long", Default = false, HelpText = "Uses long format.")]
|
[Option('l', "long", Default = false, HelpText = "Uses long format.")]
|
||||||
public bool Long { get; set; }
|
public bool Long { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Verb("extract-files", HelpText = "Extracts all files in disc image.")]
|
||||||
|
public class ExtractFilesOptions : CommonOptions
|
||||||
|
{
|
||||||
|
[Option('i', "input", Required = true, HelpText = "Disc image.")]
|
||||||
|
public string InputFile { get; set; }
|
||||||
|
|
||||||
|
[Option('o', "output", Required = true, HelpText = "Directory where extracted files will be created. Will abort if it exists.")]
|
||||||
|
public string OutputDir { get; set; }
|
||||||
|
|
||||||
|
[Option('x', "xattrs", Default = false, HelpText = "Extract extended attributes if present.")]
|
||||||
|
public bool Xattrs { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user