mirror of
https://github.com/aaru-dps/Aaru.git
synced 2025-12-16 19:24:25 +00:00
Added comments.
This commit is contained in:
@@ -1,3 +1,16 @@
|
||||
2016-07-29 Natalia Portillo <claunia@claunia.com>
|
||||
|
||||
* Filesystem.cs:
|
||||
* Dir.cs:
|
||||
* File.cs:
|
||||
* Super.cs:
|
||||
* Xattr.cs:
|
||||
* LisaFS.cs:
|
||||
* Consts.cs:
|
||||
* Extent.cs:
|
||||
* Structs.cs:
|
||||
* Encoding.cs: Added comments.
|
||||
|
||||
2016-07-28 Natalia Portillo <claunia@claunia.com>
|
||||
|
||||
* Dir.cs:
|
||||
|
||||
@@ -133,7 +133,7 @@ namespace DiscImageChef.Filesystems
|
||||
/// <summary>
|
||||
/// Reads an extended attribute, alternate data stream or fork from the given file.
|
||||
/// </summary>
|
||||
/// <returns>The extended attribute, alternate data stream or fork name.</returns>
|
||||
/// <returns>Error number.</returns>
|
||||
/// <param name="path">File path.</param>
|
||||
/// <param name="xattr">Extendad attribute, alternate data stream or fork name.</param>
|
||||
/// <param name="buf">Buffer.</param>
|
||||
|
||||
@@ -34,41 +34,126 @@ namespace DiscImageChef.Filesystems.LisaFS
|
||||
{
|
||||
partial class LisaFS : Filesystem
|
||||
{
|
||||
/// <summary>
|
||||
/// Lisa FS v1, from Lisa OS 1.0 (Workshop or Office)
|
||||
/// Never seen on Sony floppies.
|
||||
/// </summary>
|
||||
const byte LisaFSv1 = 0x0E;
|
||||
/// <summary>
|
||||
/// Lisa FS v2, from Lisa OS 2.0 (Workshop or Office)
|
||||
/// Contrary to what most information online says the only difference with V1
|
||||
/// is the Extents File size. Catalog format is the same
|
||||
/// </summary>
|
||||
const byte LisaFSv2 = 0x0F;
|
||||
/// <summary>
|
||||
/// Lisa FS v3, from Lisa OS 3.0 (Workshop or Office)
|
||||
/// Adds support for user catalogs (aka subdirectories),
|
||||
/// and changes the catalog format from extents to double-linked list.
|
||||
/// Uses '-' as path separator (so people that created Lisa/FILE.TEXT just
|
||||
/// created a file named like that :p)
|
||||
/// </summary>
|
||||
const byte LisaFSv3 = 0x11;
|
||||
/// <summary>Maximum string size in LisaFS</summary>
|
||||
const uint E_NAME = 32;
|
||||
/// <summary>
|
||||
/// Unused file ID
|
||||
/// </summary>
|
||||
const ushort FILEID_FREE = 0x0000;
|
||||
/// <summary>
|
||||
/// Used by the boot blocks
|
||||
/// </summary>
|
||||
const ushort FILEID_BOOT = 0xAAAA;
|
||||
/// <summary>
|
||||
/// Used by the operating system loader blocks
|
||||
/// </summary>
|
||||
const ushort FILEID_LOADER = 0xBBBB;
|
||||
/// <summary>
|
||||
/// Used by the MDDF
|
||||
/// </summary>
|
||||
const ushort FILEID_MDDF = 0x0001;
|
||||
/// <summary>
|
||||
/// Used by the volume bitmap, sits between MDDF and S-Records file.
|
||||
/// </summary>
|
||||
const ushort FILEID_BITMAP = 0x0002;
|
||||
/// <summary>
|
||||
/// S-Records file
|
||||
/// </summary>
|
||||
const ushort FILEID_SRECORD = 0x0003;
|
||||
/// <summary>"Catalog file"</summary>
|
||||
const ushort FILEID_DIRECTORY = 0x0004;
|
||||
/// <summary>The root catalog</summary>
|
||||
const ushort FILEID_ROOTCATALOG = 0x0004;
|
||||
const short FILEID_BOOT_SIGNED = -21846;
|
||||
const short FILEID_LOADER_SIGNED = -17477;
|
||||
/// <summary>
|
||||
/// A file that has been erased
|
||||
/// </summary>
|
||||
const ushort FILEID_ERASED = 0x7FFF;
|
||||
const ushort FILEID_MAX = FILEID_ERASED;
|
||||
|
||||
enum FileType : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Undefined file type
|
||||
/// </summary>
|
||||
Undefined = 0,
|
||||
/// <summary>
|
||||
/// MDDF
|
||||
/// </summary>
|
||||
MDDFile = 1,
|
||||
/// <summary>
|
||||
/// Root catalog
|
||||
/// </summary>
|
||||
RootCat = 2,
|
||||
/// <summary>
|
||||
/// Bitmap
|
||||
/// </summary>
|
||||
FreeList = 3,
|
||||
/// <summary>
|
||||
/// Unknown, maybe refers to the S-Records File?
|
||||
/// </summary>
|
||||
BadBlocks = 4,
|
||||
/// <summary>
|
||||
/// System data
|
||||
/// </summary>
|
||||
SysData = 5,
|
||||
/// <summary>
|
||||
/// Printer spool
|
||||
/// </summary>
|
||||
Spool = 6,
|
||||
/// <summary>
|
||||
/// Executable. Yet application files don't use it
|
||||
/// </summary>
|
||||
Exec = 7,
|
||||
/// <summary>
|
||||
/// User catalog
|
||||
/// </summary>
|
||||
UserCat = 8,
|
||||
/// <summary>
|
||||
/// Pipe. Not seen on disk.
|
||||
/// </summary>
|
||||
Pipe = 9,
|
||||
/// <summary>
|
||||
/// Boot file?
|
||||
/// </summary>
|
||||
BootFile = 10,
|
||||
/// <summary>
|
||||
/// Swap for data
|
||||
/// </summary>
|
||||
SwapData = 11,
|
||||
/// <summary>
|
||||
/// Swap for code
|
||||
/// </summary>
|
||||
SwapCode = 12,
|
||||
/// <summary>
|
||||
/// Unknown
|
||||
/// </summary>
|
||||
RamAP = 13,
|
||||
/// <summary>
|
||||
/// Any file
|
||||
/// </summary>
|
||||
UserFile = 14,
|
||||
/// <summary>
|
||||
/// Erased?
|
||||
/// </summary>
|
||||
KilledObject = 15
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,12 +38,22 @@ namespace DiscImageChef.Filesystems.LisaFS
|
||||
{
|
||||
partial class LisaFS : Filesystem
|
||||
{
|
||||
/// <summary>
|
||||
/// Solves a symbolic link.
|
||||
/// </summary>
|
||||
/// <param name="path">Link path.</param>
|
||||
/// <param name="dest">Link destination.</param>
|
||||
public override Errno ReadLink(string path, ref string dest)
|
||||
{
|
||||
// LisaFS does not support symbolic links (afaik)
|
||||
return Errno.NotSupported;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lists contents from a directory.
|
||||
/// </summary>
|
||||
/// <param name="path">Directory path.</param>
|
||||
/// <param name="contents">Directory contents.</param>
|
||||
public override Errno ReadDir(string path, ref List<string> contents)
|
||||
{
|
||||
short fileId;
|
||||
@@ -58,10 +68,14 @@ namespace DiscImageChef.Filesystems.LisaFS
|
||||
List<CatalogEntry> catalog;
|
||||
ReadCatalog(fileId, out catalog);
|
||||
|
||||
// Do same trick as Mac OS X, replace filesystem '/' with ':'
|
||||
// Maybe as ':' is the path separator in Lisa OS I should do that
|
||||
foreach(CatalogEntry entry in catalog)
|
||||
contents.Add(GetString(entry.filename).Replace('/', ':'));
|
||||
|
||||
if(debug && fileId == FILEID_DIRECTORY)
|
||||
// On debug add system files as readable files
|
||||
// Syntax similar to NTFS
|
||||
if(debug && fileId == FILEID_ROOTCATALOG)
|
||||
{
|
||||
contents.Add("$MDDF");
|
||||
contents.Add("$Boot");
|
||||
@@ -75,6 +89,11 @@ namespace DiscImageChef.Filesystems.LisaFS
|
||||
return Errno.NoError;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lists contents from a catalog.
|
||||
/// </summary>
|
||||
/// <param name="fileId">Catalog id.</param>
|
||||
/// <param name="catalog">Catalog contents.</param>
|
||||
Errno ReadCatalog(short fileId, out List<CatalogEntry> catalog)
|
||||
{
|
||||
catalog = null;
|
||||
@@ -90,11 +109,14 @@ namespace DiscImageChef.Filesystems.LisaFS
|
||||
|
||||
Errno error;
|
||||
|
||||
// Do differently for V1 and V2
|
||||
if(mddf.fsversion == LisaFSv2 || mddf.fsversion == LisaFSv1)
|
||||
{
|
||||
if(fileId != FILEID_DIRECTORY)
|
||||
// V1 and V2 can only contain the root catalog
|
||||
if(fileId != FILEID_ROOTCATALOG)
|
||||
{
|
||||
ExtentFile ext;
|
||||
// Check if it's a file to return correct error
|
||||
error = ReadExtentsFile(fileId, out ext);
|
||||
if(error == Errno.NoError)
|
||||
return Errno.NotDirectory;
|
||||
@@ -106,6 +128,7 @@ namespace DiscImageChef.Filesystems.LisaFS
|
||||
int offset = 0;
|
||||
List<CatalogEntryV2> catalogV2 = new List<CatalogEntryV2>();
|
||||
|
||||
// For each entry on the catalog
|
||||
while(offset + 54 < buf.Length)
|
||||
{
|
||||
CatalogEntryV2 entV2 = new CatalogEntryV2();
|
||||
@@ -121,12 +144,14 @@ namespace DiscImageChef.Filesystems.LisaFS
|
||||
|
||||
offset += 54;
|
||||
|
||||
// Check that the entry is correct, not empty or garbage
|
||||
if(entV2.filenameLen != 0 && entV2.filenameLen <= E_NAME && entV2.fileType != 0 && entV2.fileID > 0)
|
||||
catalogV2.Add(entV2);
|
||||
}
|
||||
|
||||
catalog = new List<CatalogEntry>();
|
||||
|
||||
// Convert entries to V3 format
|
||||
foreach(CatalogEntryV2 entV2 in catalogV2)
|
||||
{
|
||||
ExtentFile ext;
|
||||
@@ -152,6 +177,9 @@ namespace DiscImageChef.Filesystems.LisaFS
|
||||
|
||||
byte[] firstCatalogBlock = null;
|
||||
|
||||
// Search for the first sector describing the catalog
|
||||
// While root catalog is not stored in S-Records, probably rest are? (unchecked)
|
||||
// If root catalog is not pointed in MDDF (unchecked) maybe it's always following S-Records File?
|
||||
for(ulong i = 0; i < device.GetSectors(); i++)
|
||||
{
|
||||
Tag catTag;
|
||||
@@ -163,16 +191,19 @@ namespace DiscImageChef.Filesystems.LisaFS
|
||||
break;
|
||||
}
|
||||
|
||||
// Found Extents File for a catalog, not allowable in V3, at least for root catalog
|
||||
if(catTag.fileID == -fileId)
|
||||
return Errno.NotDirectory;
|
||||
}
|
||||
|
||||
// Catalog not found
|
||||
if(firstCatalogBlock == null)
|
||||
return Errno.NoSuchFile;
|
||||
|
||||
ulong prevCatalogPointer;
|
||||
prevCatalogPointer = BigEndianBitConverter.ToUInt32(firstCatalogBlock, 0x7F6);
|
||||
|
||||
// Traverse double-linked list until first catalog block
|
||||
while(prevCatalogPointer != 0xFFFFFFFF)
|
||||
{
|
||||
Tag prevTag;
|
||||
@@ -191,6 +222,7 @@ namespace DiscImageChef.Filesystems.LisaFS
|
||||
List<byte[]> catalogBlocks = new List<byte[]>();
|
||||
catalogBlocks.Add(firstCatalogBlock);
|
||||
|
||||
// Traverse double-linked list to read full catalog
|
||||
while(nextCatalogPointer != 0xFFFFFFFF)
|
||||
{
|
||||
Tag nextTag;
|
||||
@@ -206,18 +238,24 @@ namespace DiscImageChef.Filesystems.LisaFS
|
||||
|
||||
catalog = new List<CatalogEntry>();
|
||||
|
||||
// Foreach catalog block
|
||||
foreach(byte[] buf in catalogBlocks)
|
||||
{
|
||||
int offset = 0;
|
||||
|
||||
// Traverse all entries
|
||||
while((offset + 64) <= buf.Length)
|
||||
{
|
||||
// Catalog block header
|
||||
if(buf[offset + 0x24] == 0x08)
|
||||
offset += 78;
|
||||
// Maybe just garbage? Found in more than 1 disk
|
||||
else if(buf[offset + 0x24] == 0x7C)
|
||||
offset += 50;
|
||||
// Apparently reserved to indicate end of catalog?
|
||||
else if(buf[offset + 0x24] == 0xFF)
|
||||
break;
|
||||
// Normal entry
|
||||
else if(buf[offset + 0x24] == 0x03 && buf[offset] == 0x24)
|
||||
{
|
||||
CatalogEntry entry = new CatalogEntry();
|
||||
|
||||
@@ -105,11 +105,21 @@ namespace DiscImageChef.Filesystems.LisaFS
|
||||
'\u0000','\u0000','\u0000','\u0000','\u0000','\u0000','\u0000','\u0000'
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Converts a LisaRoman character to an Unicode character
|
||||
/// </summary>
|
||||
/// <returns>Unicode character.</returns>
|
||||
/// <param name="character">LisaRoman character.</param>
|
||||
static char GetChar(byte character)
|
||||
{
|
||||
return LisaRomanTable[character];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a LisaRoman string, null-terminated or null-paded, to a C# string
|
||||
/// </summary>
|
||||
/// <returns>The C# string.</returns>
|
||||
/// <param name="str">LisaRoman string.</param>
|
||||
static string GetString(byte[] str)
|
||||
{
|
||||
string uni = "";
|
||||
@@ -125,6 +135,11 @@ namespace DiscImageChef.Filesystems.LisaFS
|
||||
return uni;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a LisaRoman string, in Pascal length-prefixed format, to a C# string
|
||||
/// </summary>
|
||||
/// <returns>The C# string.</returns>
|
||||
/// <param name="PascalString">The LisaRoman string in Pascal format.</param>
|
||||
static string GetStringFromPascal(byte[] PascalString)
|
||||
{
|
||||
if(PascalString == null)
|
||||
|
||||
@@ -63,21 +63,24 @@ namespace DiscImageChef.Filesystems.LisaFS
|
||||
if(extentCache.TryGetValue(fileId, out file))
|
||||
return Errno.NoError;
|
||||
|
||||
// A file ID that cannot be stored in the S-Records File
|
||||
if(fileId >= srecords.Length)
|
||||
return Errno.InvalidArgument;
|
||||
|
||||
ulong ptr = srecords[fileId].extent_ptr;
|
||||
|
||||
// An invalid pointer denotes file does not exist
|
||||
if(ptr == 0xFFFFFFFF || ptr == 0x00000000)
|
||||
return Errno.NoSuchFile;
|
||||
|
||||
// Pointers are relative to MDDF
|
||||
ptr += mddf.mddf_block + volumePrefix;
|
||||
|
||||
Tag extTag;
|
||||
|
||||
// This happens on some disks.
|
||||
// This is a filesystem corruption that makes LisaOS crash on scavenge.
|
||||
// This code just allow to ignore that corruption
|
||||
// This code just allow to ignore that corruption by searching the Extents File using sector tags
|
||||
if(ptr >= device.ImageInfo.sectors)
|
||||
{
|
||||
bool found = false;
|
||||
@@ -96,6 +99,7 @@ namespace DiscImageChef.Filesystems.LisaFS
|
||||
return Errno.InvalidArgument;
|
||||
}
|
||||
|
||||
// Checks that the sector tag indicates its the Extents File we are searching for
|
||||
DecodeTag(device.ReadSectorTag(ptr, SectorTagType.AppleSectorTag), out extTag);
|
||||
|
||||
if(extTag.fileID == ((short)(-1 * fileId)))
|
||||
@@ -249,13 +253,18 @@ namespace DiscImageChef.Filesystems.LisaFS
|
||||
return Errno.NoSuchFile;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads all the S-Records and caches it
|
||||
/// </summary>
|
||||
Errno ReadSRecords()
|
||||
{
|
||||
if(!mounted)
|
||||
return Errno.AccessDenied;
|
||||
|
||||
// Searches the S-Records place using MDDF pointers
|
||||
byte[] sectors = device.ReadSectors(mddf.srec_ptr + mddf.mddf_block + volumePrefix, mddf.srec_len);
|
||||
|
||||
// Each entry takes 14 bytes
|
||||
srecords = new SRecord[sectors.Length / 14];
|
||||
|
||||
for(int s = 0; s < srecords.Length; s++)
|
||||
|
||||
@@ -76,7 +76,7 @@ namespace DiscImageChef.Filesystems.LisaFS
|
||||
case (short)FILEID_MDDF:
|
||||
case (short)FILEID_BITMAP:
|
||||
case (short)FILEID_SRECORD:
|
||||
case (short)FILEID_DIRECTORY:
|
||||
case (short)FILEID_ROOTCATALOG:
|
||||
error = ReadSystemFile(fileId, out tmp);
|
||||
break;
|
||||
default:
|
||||
@@ -453,7 +453,7 @@ namespace DiscImageChef.Filesystems.LisaFS
|
||||
|
||||
if(pathElements.Length == 0)
|
||||
{
|
||||
fileId = (short)FILEID_DIRECTORY;
|
||||
fileId = (short)FILEID_ROOTCATALOG;
|
||||
isDir = true;
|
||||
return Errno.NoError;
|
||||
}
|
||||
@@ -496,7 +496,7 @@ namespace DiscImageChef.Filesystems.LisaFS
|
||||
|
||||
if(string.Compare(pathElements[0], "$", StringComparison.InvariantCulture) == 0)
|
||||
{
|
||||
fileId = (short)FILEID_DIRECTORY;
|
||||
fileId = (short)FILEID_ROOTCATALOG;
|
||||
isDir = true;
|
||||
return Errno.NoError;
|
||||
}
|
||||
@@ -504,7 +504,7 @@ namespace DiscImageChef.Filesystems.LisaFS
|
||||
|
||||
List<CatalogEntry> catalog;
|
||||
|
||||
Errno error = ReadCatalog((short)FILEID_DIRECTORY, out catalog);
|
||||
Errno error = ReadCatalog((short)FILEID_ROOTCATALOG, out catalog);
|
||||
if(error != Errno.NoError)
|
||||
return error;
|
||||
|
||||
|
||||
@@ -51,11 +51,17 @@ namespace DiscImageChef.Filesystems.LisaFS
|
||||
SRecord[] srecords;
|
||||
|
||||
#region Caches
|
||||
/// <summary>Caches Extents Files</summary>
|
||||
Dictionary<short, ExtentFile> extentCache;
|
||||
/// <summary>Caches system files</summary>
|
||||
Dictionary<short, byte[]> systemFileCache;
|
||||
/// <summary>Caches user files files</summary>
|
||||
Dictionary<short, byte[]> fileCache;
|
||||
/// <summary>Caches catalogs</summary>
|
||||
Dictionary<short, List<CatalogEntry>> catalogCache;
|
||||
/// <summary>Caches file size</summary>
|
||||
Dictionary<short, int> fileSizeCache;
|
||||
/// <summary>Lists Extents Files already printed in debug mode to not repeat them</summary>
|
||||
List<short> printedExtents;
|
||||
#endregion Caches
|
||||
|
||||
|
||||
@@ -35,6 +35,13 @@ namespace DiscImageChef.Filesystems.LisaFS
|
||||
{
|
||||
partial class LisaFS : Filesystem
|
||||
{
|
||||
/// <summary>
|
||||
/// The MDDF is the most import block on a Lisa FS volume.
|
||||
/// It describes the volume and its contents.
|
||||
/// On initialization the memory where it resides is not emptied
|
||||
/// so it tends to contain a lot of garbage. This has difficulted
|
||||
/// its reverse engineering.
|
||||
/// </summary>
|
||||
struct MDDF
|
||||
{
|
||||
/// <summary>0x00, Filesystem version</summary>
|
||||
@@ -200,6 +207,13 @@ namespace DiscImageChef.Filesystems.LisaFS
|
||||
public byte scavenge_flag;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The sector tag. Before the sector is encoded to GCR the tag is attached to the data.
|
||||
/// Its size and format varies depending on device.
|
||||
/// Lisa OS relies on tags for scavenging a floppy, but ignores most of them for normal usage,
|
||||
/// except on hard disks where the checksum byte is absolutely enforced (a sector with an invalid
|
||||
/// one gives an OS error).
|
||||
/// </summary>
|
||||
struct Tag
|
||||
{
|
||||
/// <summary>0x00 version</summary>
|
||||
@@ -235,11 +249,17 @@ namespace DiscImageChef.Filesystems.LisaFS
|
||||
public bool isLast;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An entry in the catalog from V3.
|
||||
/// The first entry is bigger than the rest, may be a header, I have not needed any of its values so I just ignored it.
|
||||
/// Each catalog is divided in 4-sector blocks, and if it needs more than a block there are previous and next block
|
||||
/// pointers, effectively making the V3 catalog a double-linked list. Garbage is not zeroed.
|
||||
/// </summary>
|
||||
struct CatalogEntry
|
||||
{
|
||||
/// <summary>0x00, seems to be 0x24 when the entry is valid</summary>
|
||||
public byte marker;
|
||||
/// <summary>0x01, seems to be always zero</summary>
|
||||
/// <summary>0x01, must be zero otherwise LisaOS gives an error</summary>
|
||||
public ushort zero;
|
||||
/// <summary>0x03, filename, 32-bytes, null-padded</summary>
|
||||
public byte[] filename;
|
||||
@@ -249,7 +269,6 @@ namespace DiscImageChef.Filesystems.LisaFS
|
||||
/// At 0x24
|
||||
/// 0x03 here for entries 64 bytes long
|
||||
/// 0x08 here for entries 78 bytes long
|
||||
/// 0x7C here for entries 50 bytes long
|
||||
/// This is incomplete, may fail, mostly works...
|
||||
/// </summary>
|
||||
public byte fileType;
|
||||
@@ -269,12 +288,23 @@ namespace DiscImageChef.Filesystems.LisaFS
|
||||
public byte[] tail;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An extent indicating a start and a run of sectors.
|
||||
/// </summary>
|
||||
struct Extent
|
||||
{
|
||||
public int start;
|
||||
public short length;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The Extents File. There is one Extents File per each file stored on disk.
|
||||
/// The file ID present on the sectors tags for the Extents File is the negated
|
||||
/// value of the file ID it represents. e.g. file = 5 (0x0005) extents = -5 (0xFFFB)
|
||||
/// It spans a single sector on V2 and V3 but 2 sectors on V1.
|
||||
/// It contains all information about a file, and is indexed in the S-Records file.
|
||||
/// It also contains the label. Garbage is zeroed.
|
||||
/// </summary>
|
||||
struct ExtentFile
|
||||
{
|
||||
/// <summary>0x00, filename length</summary>
|
||||
@@ -349,10 +379,26 @@ namespace DiscImageChef.Filesystems.LisaFS
|
||||
public Extent[] extents;
|
||||
/// <summary>0x17E, unknown, empty, padding?</summary>
|
||||
public short unknown10;
|
||||
/// <summary>0x180, 128 bytes</summary>
|
||||
/// <summary>
|
||||
/// At 0x180, this is the label.
|
||||
/// While 1982 pre-release documentation says the label can be up to 448 bytes, v1 onward only have space for a 128 bytes one.
|
||||
/// Any application can write whatever they want in the label, however, Lisa Office uses it to store its own information, something
|
||||
/// that will effectively overwrite any information a user application wrote there.
|
||||
/// The information written here by Lisa Office is like the information Finder writes in the FinderInfo structures, plus
|
||||
/// the non-unique name that is shown on the GUI. For this reason I called it LisaInfo.
|
||||
/// I have not tried to reverse engineer it.
|
||||
/// </summary>
|
||||
public byte[] LisaInfo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The S-Records File is a hashtable of S-Records, where the hash is the file ID they belong to.
|
||||
/// The S-Records File cannot be fragmented or grown, and it can easily become full before the 32766 file IDs are exhausted.
|
||||
/// Each S-Record entry contains a block pointer to the Extents File that correspond to that file ID as well as the real file size,
|
||||
/// the only important information about a file that's not inside the Extents File.
|
||||
/// It also contains a low value (less than 0x200) variable field of unknown meaning and another one that seems to be flags,
|
||||
/// with values like 0, 1, 3 and 5.
|
||||
/// </summary>
|
||||
struct SRecord
|
||||
{
|
||||
/// <summary>0x00, block where ExtentsFile for this entry resides</summary>
|
||||
@@ -365,6 +411,16 @@ namespace DiscImageChef.Filesystems.LisaFS
|
||||
public ushort flags;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The catalog entry for the V1 and V2 volume formats.
|
||||
/// It merely contains the file name, type and ID, plus a few (mostly empty) unknown fields.
|
||||
/// Contrary to V3, it has no header and instead of being a double-linked list it is fragmented using an Extents File.
|
||||
/// The Extents File position for the root catalog is then stored in the S-Records File.
|
||||
/// Its entries are not filed sequentially denoting some kind of in-memory structure while at the same time
|
||||
/// forcing LisaOS to read the whole catalog. That or I missed the pointers.
|
||||
/// Empty entries just contain a 0-len filename. Garbage is not zeroed.
|
||||
/// </summary>
|
||||
struct CatalogEntryV2
|
||||
{
|
||||
/// <summary>0x00, filename, 32-bytes, null-padded</summary>
|
||||
|
||||
@@ -39,15 +39,24 @@ namespace DiscImageChef.Filesystems.LisaFS
|
||||
{
|
||||
partial class LisaFS : Filesystem
|
||||
{
|
||||
/// <summary>
|
||||
/// Mounts an Apple Lisa filesystem
|
||||
/// </summary>
|
||||
public override Errno Mount()
|
||||
{
|
||||
return Mount(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Mounts an Apple Lisa filesystem
|
||||
/// </summary>
|
||||
public override Errno Mount(bool debug)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Lisa OS is unable to work on disks without tags.
|
||||
// This code is designed like that.
|
||||
// However with some effort the code may be modified to ignore them.
|
||||
if(device.ImageInfo.readableSectorTags == null ||
|
||||
!device.ImageInfo.readableSectorTags.Contains(SectorTagType.AppleSectorTag))
|
||||
{
|
||||
@@ -170,6 +179,7 @@ namespace DiscImageChef.Filesystems.LisaFS
|
||||
mddf.vol_sequence = BigEndianBitConverter.ToUInt16(sector, 0x136);
|
||||
mddf.vol_left_mounted = sector[0x138];
|
||||
|
||||
// Check that the MDDF is correct
|
||||
if(mddf.mddf_block != i - volumePrefix ||
|
||||
mddf.vol_size > device.GetSectors() ||
|
||||
mddf.vol_size - 1 != mddf.volsize_minus_one ||
|
||||
@@ -182,6 +192,7 @@ namespace DiscImageChef.Filesystems.LisaFS
|
||||
return Errno.InvalidArgument;
|
||||
}
|
||||
|
||||
// Check MDDF version
|
||||
switch(mddf.fsversion)
|
||||
{
|
||||
case LisaFSv1:
|
||||
@@ -198,6 +209,7 @@ namespace DiscImageChef.Filesystems.LisaFS
|
||||
return Errno.NotSupported;
|
||||
}
|
||||
|
||||
// Initialize caches
|
||||
extentCache = new Dictionary<short, ExtentFile>();
|
||||
systemFileCache = new Dictionary<short, byte[]>();
|
||||
fileCache = new Dictionary<short, byte[]>();
|
||||
@@ -214,6 +226,7 @@ namespace DiscImageChef.Filesystems.LisaFS
|
||||
printedExtents = new List<short>();
|
||||
}
|
||||
|
||||
// Read the S-Records file
|
||||
error = ReadSRecords();
|
||||
if(error != Errno.NoError)
|
||||
{
|
||||
@@ -221,8 +234,9 @@ namespace DiscImageChef.Filesystems.LisaFS
|
||||
return error;
|
||||
}
|
||||
|
||||
// Read the root catalog
|
||||
List<CatalogEntry> tempCat;
|
||||
error = ReadCatalog((short)FILEID_DIRECTORY, out tempCat);
|
||||
error = ReadCatalog((short)FILEID_ROOTCATALOG, out tempCat);
|
||||
|
||||
if(error != Errno.NoError)
|
||||
{
|
||||
@@ -231,6 +245,7 @@ namespace DiscImageChef.Filesystems.LisaFS
|
||||
return error;
|
||||
}
|
||||
|
||||
// If debug, cache system files
|
||||
if(debug)
|
||||
{
|
||||
byte[] temp;
|
||||
@@ -276,6 +291,7 @@ namespace DiscImageChef.Filesystems.LisaFS
|
||||
}
|
||||
}
|
||||
|
||||
// Create XML metadata for mounted filesystem
|
||||
xmlFSType = new Schemas.FileSystemType();
|
||||
if(DateTime.Compare(mddf.dtvb, DateHandlers.LisaToDateTime(0)) > 0)
|
||||
{
|
||||
@@ -312,6 +328,9 @@ namespace DiscImageChef.Filesystems.LisaFS
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Umounts this Lisa filesystem
|
||||
/// </summary>
|
||||
public override Errno Unmount()
|
||||
{
|
||||
mounted = false;
|
||||
@@ -329,6 +348,10 @@ namespace DiscImageChef.Filesystems.LisaFS
|
||||
return Errno.NoError;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets information about the mounted volume.
|
||||
/// </summary>
|
||||
/// <param name="stat">Information about the mounted volume.</param>
|
||||
public override Errno StatFs(ref FileSystemInfo stat)
|
||||
{
|
||||
if(!mounted)
|
||||
|
||||
@@ -39,6 +39,12 @@ namespace DiscImageChef.Filesystems.LisaFS
|
||||
{
|
||||
partial class LisaFS : Filesystem
|
||||
{
|
||||
/// <summary>
|
||||
/// Lists all extended attributes, alternate data streams and forks of the given file.
|
||||
/// </summary>
|
||||
/// <returns>Error number.</returns>
|
||||
/// <param name="path">Path.</param>
|
||||
/// <param name="xattrs">List of extended attributes, alternate data streams and forks.</param>
|
||||
public override Errno ListXAttr(string path, ref List<string> xattrs)
|
||||
{
|
||||
short fileId;
|
||||
@@ -49,6 +55,13 @@ namespace DiscImageChef.Filesystems.LisaFS
|
||||
return ListXAttr(fileId, ref xattrs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads an extended attribute, alternate data stream or fork from the given file.
|
||||
/// </summary>
|
||||
/// <returns>Error number.</returns>
|
||||
/// <param name="path">File path.</param>
|
||||
/// <param name="xattr">Extendad attribute, alternate data stream or fork name.</param>
|
||||
/// <param name="buf">Buffer.</param>
|
||||
public override Errno GetXattr(string path, string xattr, ref byte[] buf)
|
||||
{
|
||||
short fileId;
|
||||
@@ -59,6 +72,12 @@ namespace DiscImageChef.Filesystems.LisaFS
|
||||
return GetXattr(fileId, xattr, out buf);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lists special Apple Lisa filesystem features as extended attributes
|
||||
/// </summary>
|
||||
/// <returns>Error number.</returns>
|
||||
/// <param name="fileId">File identifier.</param>
|
||||
/// <param name="xattrs">Extended attributes.</param>
|
||||
Errno ListXAttr(short fileId, ref List<string> xattrs)
|
||||
{
|
||||
xattrs = null;
|
||||
@@ -66,6 +85,7 @@ namespace DiscImageChef.Filesystems.LisaFS
|
||||
if(!mounted)
|
||||
return Errno.AccessDenied;
|
||||
|
||||
// System files
|
||||
if(fileId < 4)
|
||||
{
|
||||
if(!debug || fileId == 0)
|
||||
@@ -73,34 +93,41 @@ namespace DiscImageChef.Filesystems.LisaFS
|
||||
|
||||
xattrs = new List<string>();
|
||||
|
||||
// Only MDDF contains an extended attributes
|
||||
if(fileId == FILEID_MDDF)
|
||||
{
|
||||
byte[] buf = Encoding.ASCII.GetBytes(mddf.password);
|
||||
|
||||
// If the MDDF contains a password, show it
|
||||
if(buf.Length > 0)
|
||||
xattrs.Add("com.apple.lisa.password");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
// Search for the file
|
||||
ExtentFile file;
|
||||
|
||||
Errno error = ReadExtentsFile(fileId, out file);
|
||||
|
||||
if(error != Errno.NoError)
|
||||
return error;
|
||||
|
||||
xattrs = new List<string>();
|
||||
|
||||
// Password field is never emptied, check if valid
|
||||
if(file.password_valid > 0)
|
||||
xattrs.Add("com.apple.lisa.password");
|
||||
|
||||
// Check for a valid copy-protection serial number
|
||||
if(file.serial > 0)
|
||||
xattrs.Add("com.apple.lisa.serial");
|
||||
|
||||
// Check if the label contains something or is empty
|
||||
if(!ArrayHelpers.ArrayIsNullOrEmpty(file.LisaInfo))
|
||||
xattrs.Add("com.apple.lisa.label");
|
||||
}
|
||||
|
||||
// On debug mode allow sector tags to be accessed as an xattr
|
||||
if(debug)
|
||||
xattrs.Add("com.apple.lisa.tags");
|
||||
|
||||
@@ -109,6 +136,13 @@ namespace DiscImageChef.Filesystems.LisaFS
|
||||
return Errno.NoError;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lists special Apple Lisa filesystem features as extended attributes
|
||||
/// </summary>
|
||||
/// <returns>Error number.</returns>
|
||||
/// <param name="fileId">File identifier.</param>
|
||||
/// <param name="xattr">Extended attribute name.</param>
|
||||
/// <param name="buf">Buffer where the extended attribute will be stored.</param>
|
||||
Errno GetXattr(short fileId, string xattr, out byte[] buf)
|
||||
{
|
||||
buf = null;
|
||||
@@ -116,11 +150,13 @@ namespace DiscImageChef.Filesystems.LisaFS
|
||||
if(!mounted)
|
||||
return Errno.AccessDenied;
|
||||
|
||||
// System files
|
||||
if(fileId < 4)
|
||||
{
|
||||
if(!debug || fileId == 0)
|
||||
return Errno.InvalidArgument;
|
||||
|
||||
// Only MDDF contains an extended attributes
|
||||
if(fileId == FILEID_MDDF)
|
||||
{
|
||||
if(xattr == "com.apple.lisa.password")
|
||||
@@ -130,14 +166,15 @@ namespace DiscImageChef.Filesystems.LisaFS
|
||||
}
|
||||
}
|
||||
|
||||
// But on debug mode even system files contain tags
|
||||
if(debug && xattr == "com.apple.lisa.tags")
|
||||
return ReadSystemFile(fileId, out buf, true);
|
||||
|
||||
return Errno.NoSuchExtendedAttribute;
|
||||
}
|
||||
|
||||
// Search for the file
|
||||
ExtentFile file;
|
||||
|
||||
Errno error = ReadExtentsFile(fileId, out file);
|
||||
|
||||
if(error != Errno.NoError)
|
||||
@@ -169,6 +206,12 @@ namespace DiscImageChef.Filesystems.LisaFS
|
||||
return Errno.NoSuchExtendedAttribute;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decodes a sector tag. Not tested with 24-byte tags.
|
||||
/// </summary>
|
||||
/// <returns>Error number.</returns>
|
||||
/// <param name="tag">Sector tag.</param>
|
||||
/// <param name="decoded">Decoded sector tag.</param>
|
||||
Errno DecodeTag(byte[] tag, out Tag decoded)
|
||||
{
|
||||
decoded = new Tag();
|
||||
@@ -214,4 +257,3 @@ namespace DiscImageChef.Filesystems.LisaFS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user