2019-07-31 20:10:27 +01:00
|
|
|
// /***************************************************************************
|
2020-02-27 12:31:25 +00:00
|
|
|
// Aaru Data Preservation Suite
|
2019-07-31 20:10:27 +01:00
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// Filename : Dir.cs
|
|
|
|
|
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
|
|
|
|
//
|
|
|
|
|
// Component : ISO9660 filesystem plugin.
|
|
|
|
|
//
|
|
|
|
|
// --[ Description ] ----------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// Handles directory traversal and listing.
|
|
|
|
|
//
|
|
|
|
|
// --[ License ] --------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// This library is free software; you can redistribute it and/or modify
|
|
|
|
|
// it under the terms of the GNU Lesser General Public License as
|
|
|
|
|
// published by the Free Software Foundation; either version 2.1 of the
|
|
|
|
|
// License, or (at your option) any later version.
|
|
|
|
|
//
|
|
|
|
|
// This library is distributed in the hope that it will be useful, but
|
|
|
|
|
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
|
// Lesser General Public License for more details.
|
|
|
|
|
//
|
|
|
|
|
// You should have received a copy of the GNU Lesser General Public
|
|
|
|
|
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
//
|
|
|
|
|
// ----------------------------------------------------------------------------
|
2020-12-31 23:08:23 +00:00
|
|
|
// Copyright © 2011-2021 Natalia Portillo
|
2019-07-31 20:19:22 +01:00
|
|
|
// In the loving memory of Facunda "Tata" Suárez Domínguez, R.I.P. 2019/07/24
|
2019-07-31 20:10:27 +01:00
|
|
|
// ****************************************************************************/
|
|
|
|
|
|
2019-07-19 12:14:30 +01:00
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
2019-07-19 16:20:58 +01:00
|
|
|
using System.Globalization;
|
2019-07-31 01:02:56 +01:00
|
|
|
using System.IO;
|
2019-07-19 14:26:02 +01:00
|
|
|
using System.Linq;
|
|
|
|
|
using System.Text;
|
2020-02-27 00:33:26 +00:00
|
|
|
using Aaru.CommonTypes.Structs;
|
|
|
|
|
using Aaru.Helpers;
|
2019-07-19 12:14:30 +01:00
|
|
|
|
2020-07-20 15:43:52 +01:00
|
|
|
namespace Aaru.Filesystems
|
2019-07-19 12:14:30 +01:00
|
|
|
{
|
2020-07-22 13:20:25 +01:00
|
|
|
public sealed partial class ISO9660
|
2019-07-19 12:14:30 +01:00
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
Dictionary<string, Dictionary<string, DecodedDirectoryEntry>> _directoryCache;
|
2019-07-19 16:20:58 +01:00
|
|
|
|
2019-07-19 15:44:40 +01:00
|
|
|
public Errno ReadDir(string path, out List<string> contents)
|
|
|
|
|
{
|
|
|
|
|
contents = null;
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(!_mounted)
|
2020-02-19 01:20:23 +00:00
|
|
|
return Errno.AccessDenied;
|
|
|
|
|
|
|
|
|
|
if(string.IsNullOrWhiteSpace(path) ||
|
|
|
|
|
path == "/")
|
2019-07-19 15:44:40 +01:00
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
contents = GetFilenames(_rootDirectoryCache);
|
2020-02-19 01:20:23 +00:00
|
|
|
|
2019-07-19 16:20:58 +01:00
|
|
|
return Errno.NoError;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string cutPath = path.StartsWith("/", StringComparison.Ordinal)
|
|
|
|
|
? path.Substring(1).ToLower(CultureInfo.CurrentUICulture)
|
|
|
|
|
: path.ToLower(CultureInfo.CurrentUICulture);
|
2019-07-19 15:44:40 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_directoryCache.TryGetValue(cutPath, out Dictionary<string, DecodedDirectoryEntry> currentDirectory))
|
2019-07-19 16:20:58 +01:00
|
|
|
{
|
|
|
|
|
contents = currentDirectory.Keys.ToList();
|
2020-02-19 01:20:23 +00:00
|
|
|
|
2019-07-19 15:44:40 +01:00
|
|
|
return Errno.NoError;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-19 01:20:23 +00:00
|
|
|
string[] pieces = cutPath.Split(new[]
|
|
|
|
|
{
|
|
|
|
|
'/'
|
|
|
|
|
}, StringSplitOptions.RemoveEmptyEntries);
|
2019-07-19 16:20:58 +01:00
|
|
|
|
|
|
|
|
KeyValuePair<string, DecodedDirectoryEntry> entry =
|
2020-07-20 21:11:32 +01:00
|
|
|
_rootDirectoryCache.FirstOrDefault(t => t.Key.ToLower(CultureInfo.CurrentUICulture) == pieces[0]);
|
2019-07-19 16:20:58 +01:00
|
|
|
|
2020-02-19 01:20:23 +00:00
|
|
|
if(string.IsNullOrEmpty(entry.Key))
|
|
|
|
|
return Errno.NoSuchFile;
|
2019-07-19 16:20:58 +01:00
|
|
|
|
2020-02-19 01:20:23 +00:00
|
|
|
if(!entry.Value.Flags.HasFlag(FileFlags.Directory))
|
|
|
|
|
return Errno.NotDirectory;
|
2019-07-19 16:20:58 +01:00
|
|
|
|
|
|
|
|
string currentPath = pieces[0];
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
currentDirectory = _rootDirectoryCache;
|
2019-07-19 16:20:58 +01:00
|
|
|
|
|
|
|
|
for(int p = 0; p < pieces.Length; p++)
|
|
|
|
|
{
|
|
|
|
|
entry = currentDirectory.FirstOrDefault(t => t.Key.ToLower(CultureInfo.CurrentUICulture) == pieces[p]);
|
|
|
|
|
|
2020-02-19 01:20:23 +00:00
|
|
|
if(string.IsNullOrEmpty(entry.Key))
|
|
|
|
|
return Errno.NoSuchFile;
|
2019-07-19 16:20:58 +01:00
|
|
|
|
2020-02-19 01:20:23 +00:00
|
|
|
if(!entry.Value.Flags.HasFlag(FileFlags.Directory))
|
|
|
|
|
return Errno.NotDirectory;
|
2019-07-19 16:20:58 +01:00
|
|
|
|
|
|
|
|
currentPath = p == 0 ? pieces[0] : $"{currentPath}/{pieces[p]}";
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_directoryCache.TryGetValue(currentPath, out currentDirectory))
|
2020-02-19 01:20:23 +00:00
|
|
|
continue;
|
2019-07-19 16:20:58 +01:00
|
|
|
|
2020-02-19 01:20:23 +00:00
|
|
|
if(entry.Value.Extents.Count == 0)
|
|
|
|
|
return Errno.InvalidArgument;
|
2019-07-19 16:20:58 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
currentDirectory = _cdi
|
2020-11-07 01:17:06 +00:00
|
|
|
? DecodeCdiDirectory(entry.Value.Extents[0].extent + entry.Value.XattrLength,
|
|
|
|
|
entry.Value.Extents[0].size)
|
2020-07-20 21:11:32 +01:00
|
|
|
: _highSierra
|
2020-11-07 01:17:06 +00:00
|
|
|
? DecodeHighSierraDirectory(entry.Value.Extents[0].extent + entry.Value.XattrLength,
|
|
|
|
|
entry.Value.Extents[0].size)
|
|
|
|
|
: DecodeIsoDirectory(entry.Value.Extents[0].extent + entry.Value.XattrLength,
|
|
|
|
|
entry.Value.Extents[0].size);
|
2019-07-19 16:20:58 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_usePathTable)
|
|
|
|
|
foreach(DecodedDirectoryEntry subDirectory in _cdi
|
2019-07-29 04:00:51 +01:00
|
|
|
? GetSubdirsFromCdiPathTable(currentPath)
|
2020-07-20 21:11:32 +01:00
|
|
|
: _highSierra
|
2019-07-29 04:00:51 +01:00
|
|
|
? GetSubdirsFromHighSierraPathTable(currentPath)
|
|
|
|
|
: GetSubdirsFromIsoPathTable(currentPath))
|
|
|
|
|
currentDirectory[subDirectory.Filename] = subDirectory;
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_directoryCache.Add(currentPath, currentDirectory);
|
2019-07-19 16:20:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
contents = GetFilenames(currentDirectory);
|
2020-02-19 01:20:23 +00:00
|
|
|
|
2019-07-19 16:20:58 +01:00
|
|
|
return Errno.NoError;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
List<string> GetFilenames(Dictionary<string, DecodedDirectoryEntry> dirents)
|
|
|
|
|
{
|
|
|
|
|
List<string> contents = new List<string>();
|
2020-02-19 01:20:23 +00:00
|
|
|
|
2019-07-19 16:20:58 +01:00
|
|
|
foreach(DecodedDirectoryEntry entry in dirents.Values)
|
2020-07-20 21:11:32 +01:00
|
|
|
switch(_namespace)
|
2019-07-19 16:20:58 +01:00
|
|
|
{
|
|
|
|
|
case Namespace.Normal:
|
2019-07-22 02:58:56 +01:00
|
|
|
contents.Add(entry.Filename.EndsWith(";1", StringComparison.Ordinal)
|
2020-02-19 01:20:23 +00:00
|
|
|
? entry.Filename.Substring(0, entry.Filename.Length - 2) : entry.Filename);
|
2019-07-19 16:20:58 +01:00
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
case Namespace.Vms:
|
|
|
|
|
case Namespace.Joliet:
|
|
|
|
|
case Namespace.Rrip:
|
2019-07-24 04:39:05 +01:00
|
|
|
case Namespace.Romeo:
|
|
|
|
|
contents.Add(entry.Filename);
|
2020-02-19 01:20:23 +00:00
|
|
|
|
2019-07-19 16:20:58 +01:00
|
|
|
break;
|
|
|
|
|
default: throw new ArgumentOutOfRangeException();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return contents;
|
2019-07-19 15:44:40 +01:00
|
|
|
}
|
2019-07-19 14:26:02 +01:00
|
|
|
|
2020-11-07 01:17:06 +00:00
|
|
|
Dictionary<string, DecodedDirectoryEntry> DecodeCdiDirectory(ulong start, uint size)
|
2019-07-31 05:27:22 +01:00
|
|
|
{
|
|
|
|
|
Dictionary<string, DecodedDirectoryEntry> entries = new Dictionary<string, DecodedDirectoryEntry>();
|
2020-03-10 19:31:34 +00:00
|
|
|
int entryOff = 0;
|
|
|
|
|
|
2020-11-07 01:17:06 +00:00
|
|
|
byte[] data = ReadSingleExtent(size, (uint)start);
|
2019-07-31 05:27:22 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
while(entryOff + _cdiDirectoryRecordSize < data.Length)
|
2019-07-31 05:27:22 +01:00
|
|
|
{
|
2020-03-10 19:31:34 +00:00
|
|
|
CdiDirectoryRecord record =
|
2020-07-20 21:11:32 +01:00
|
|
|
Marshal.ByteArrayToStructureBigEndian<CdiDirectoryRecord>(data, entryOff, _cdiDirectoryRecordSize);
|
2019-07-31 22:21:03 +01:00
|
|
|
|
2020-03-10 19:31:34 +00:00
|
|
|
if(record.length == 0)
|
2021-04-07 21:54:33 +01:00
|
|
|
{
|
|
|
|
|
// Skip to next sector
|
|
|
|
|
if(data.Length - entryOff >= 2048)
|
|
|
|
|
{
|
|
|
|
|
entryOff = ((data.Length % 2048) + 1) * 2048;
|
|
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-10 19:31:34 +00:00
|
|
|
break;
|
2021-04-07 21:54:33 +01:00
|
|
|
}
|
2019-07-31 05:27:22 +01:00
|
|
|
|
2020-03-10 19:31:34 +00:00
|
|
|
// Special entries for current and parent directories, skip them
|
|
|
|
|
if(record.name_len == 1)
|
2020-07-20 21:11:32 +01:00
|
|
|
if(data[entryOff + _directoryRecordSize] == 0 ||
|
|
|
|
|
data[entryOff + _directoryRecordSize] == 1)
|
2020-03-10 19:31:34 +00:00
|
|
|
{
|
|
|
|
|
entryOff += record.length;
|
2019-07-31 05:27:22 +01:00
|
|
|
|
2020-03-10 19:31:34 +00:00
|
|
|
continue;
|
|
|
|
|
}
|
2020-02-19 01:20:23 +00:00
|
|
|
|
2020-03-10 19:31:34 +00:00
|
|
|
var entry = new DecodedDirectoryEntry
|
|
|
|
|
{
|
|
|
|
|
Size = record.size,
|
2020-07-20 21:11:32 +01:00
|
|
|
Filename = Encoding.GetString(data, entryOff + _directoryRecordSize, record.name_len),
|
2020-03-10 19:31:34 +00:00
|
|
|
VolumeSequenceNumber = record.volume_sequence_number,
|
2020-07-20 04:34:16 +01:00
|
|
|
Timestamp = DecodeHighSierraDateTime(record.date),
|
|
|
|
|
XattrLength = record.xattr_len
|
2020-03-10 19:31:34 +00:00
|
|
|
};
|
2019-07-31 22:21:03 +01:00
|
|
|
|
2020-03-10 19:31:34 +00:00
|
|
|
if(record.size != 0)
|
|
|
|
|
entry.Extents = new List<(uint extent, uint size)>
|
2019-07-31 05:27:22 +01:00
|
|
|
{
|
2020-03-10 19:31:34 +00:00
|
|
|
(record.start_lbn, record.size)
|
2019-07-31 22:21:03 +01:00
|
|
|
};
|
|
|
|
|
|
2020-03-10 19:31:34 +00:00
|
|
|
if(record.flags.HasFlag(CdiFileFlags.Hidden))
|
|
|
|
|
{
|
|
|
|
|
entry.Flags |= FileFlags.Hidden;
|
2019-07-31 05:27:22 +01:00
|
|
|
|
2020-03-10 19:31:34 +00:00
|
|
|
continue;
|
|
|
|
|
}
|
2019-07-31 23:02:43 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
int systemAreaStart = entryOff + record.name_len + _cdiDirectoryRecordSize;
|
2020-02-19 01:20:23 +00:00
|
|
|
|
2020-03-10 19:31:34 +00:00
|
|
|
if(systemAreaStart % 2 != 0)
|
|
|
|
|
systemAreaStart++;
|
2019-07-31 19:39:55 +01:00
|
|
|
|
2020-03-10 19:31:34 +00:00
|
|
|
entry.CdiSystemArea =
|
2020-07-20 21:11:32 +01:00
|
|
|
Marshal.ByteArrayToStructureBigEndian<CdiSystemArea>(data, systemAreaStart, _cdiSystemAreaSize);
|
2019-07-31 05:27:22 +01:00
|
|
|
|
2020-03-10 19:31:34 +00:00
|
|
|
if(entry.CdiSystemArea.Value.attributes.HasFlag(CdiAttributes.Directory))
|
|
|
|
|
entry.Flags |= FileFlags.Directory;
|
2019-07-31 05:27:22 +01:00
|
|
|
|
2020-03-10 19:31:34 +00:00
|
|
|
if(!entry.CdiSystemArea.Value.attributes.HasFlag(CdiAttributes.Directory) ||
|
2020-07-20 21:11:32 +01:00
|
|
|
!_usePathTable)
|
2020-03-10 19:31:34 +00:00
|
|
|
entries[entry.Filename] = entry;
|
2019-07-31 17:44:34 +01:00
|
|
|
|
2020-03-10 19:31:34 +00:00
|
|
|
entryOff += record.length;
|
2019-07-31 05:27:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return entries;
|
|
|
|
|
}
|
2019-07-19 14:26:02 +01:00
|
|
|
|
2020-11-07 01:17:06 +00:00
|
|
|
Dictionary<string, DecodedDirectoryEntry> DecodeHighSierraDirectory(ulong start, uint size)
|
2019-07-20 01:42:01 +01:00
|
|
|
{
|
|
|
|
|
Dictionary<string, DecodedDirectoryEntry> entries = new Dictionary<string, DecodedDirectoryEntry>();
|
2020-03-10 19:31:34 +00:00
|
|
|
int entryOff = 0;
|
2019-07-20 01:42:01 +01:00
|
|
|
|
2020-11-07 01:17:06 +00:00
|
|
|
byte[] data = ReadSingleExtent(size, (uint)start);
|
2019-07-20 01:42:01 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
while(entryOff + _directoryRecordSize < data.Length)
|
2020-03-10 19:31:34 +00:00
|
|
|
{
|
|
|
|
|
HighSierraDirectoryRecord record =
|
|
|
|
|
Marshal.ByteArrayToStructureLittleEndian<HighSierraDirectoryRecord>(data, entryOff,
|
2020-11-07 01:17:06 +00:00
|
|
|
_highSierraDirectoryRecordSize);
|
2019-07-31 22:21:03 +01:00
|
|
|
|
2020-03-10 19:31:34 +00:00
|
|
|
if(record.length == 0)
|
2021-04-07 21:54:33 +01:00
|
|
|
{
|
|
|
|
|
// Skip to next sector
|
|
|
|
|
if(data.Length - entryOff >= 2048)
|
|
|
|
|
{
|
|
|
|
|
entryOff = ((data.Length % 2048) + 1) * 2048;
|
|
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-10 19:31:34 +00:00
|
|
|
break;
|
2021-04-07 21:54:33 +01:00
|
|
|
}
|
2019-07-31 22:21:03 +01:00
|
|
|
|
2020-03-10 19:31:34 +00:00
|
|
|
// Special entries for current and parent directories, skip them
|
|
|
|
|
if(record.name_len == 1)
|
2020-07-20 21:11:32 +01:00
|
|
|
if(data[entryOff + _directoryRecordSize] == 0 ||
|
|
|
|
|
data[entryOff + _directoryRecordSize] == 1)
|
2019-07-20 01:42:01 +01:00
|
|
|
{
|
|
|
|
|
entryOff += record.length;
|
2020-02-19 01:20:23 +00:00
|
|
|
|
2019-07-20 01:42:01 +01:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-10 19:31:34 +00:00
|
|
|
var entry = new DecodedDirectoryEntry
|
|
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
Size = record.size,
|
|
|
|
|
Flags = record.flags,
|
|
|
|
|
Interleave = record.interleave,
|
2020-03-10 19:31:34 +00:00
|
|
|
VolumeSequenceNumber = record.volume_sequence_number,
|
2020-07-20 21:11:32 +01:00
|
|
|
Filename = Encoding.GetString(data, entryOff + _directoryRecordSize, record.name_len),
|
2020-07-20 04:34:16 +01:00
|
|
|
Timestamp = DecodeHighSierraDateTime(record.date),
|
|
|
|
|
XattrLength = record.xattr_len
|
2020-03-10 19:31:34 +00:00
|
|
|
};
|
2019-07-31 22:21:03 +01:00
|
|
|
|
2020-03-10 19:31:34 +00:00
|
|
|
if(record.size != 0)
|
|
|
|
|
entry.Extents = new List<(uint extent, uint size)>
|
|
|
|
|
{
|
|
|
|
|
(record.extent, record.size)
|
|
|
|
|
};
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(entry.Flags.HasFlag(FileFlags.Directory) && _usePathTable)
|
2020-03-10 19:31:34 +00:00
|
|
|
{
|
2019-07-29 04:00:51 +01:00
|
|
|
entryOff += record.length;
|
2020-03-10 19:31:34 +00:00
|
|
|
|
|
|
|
|
continue;
|
2019-07-29 04:00:51 +01:00
|
|
|
}
|
|
|
|
|
|
2020-03-10 19:31:34 +00:00
|
|
|
if(!entries.ContainsKey(entry.Filename))
|
|
|
|
|
entries.Add(entry.Filename, entry);
|
|
|
|
|
|
|
|
|
|
entryOff += record.length;
|
2019-07-20 01:42:01 +01:00
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_useTransTbl)
|
2020-02-19 01:20:23 +00:00
|
|
|
DecodeTransTable(entries);
|
2019-07-31 01:02:56 +01:00
|
|
|
|
2019-07-20 01:42:01 +01:00
|
|
|
return entries;
|
|
|
|
|
}
|
2019-07-19 14:26:02 +01:00
|
|
|
|
2020-11-07 01:17:06 +00:00
|
|
|
Dictionary<string, DecodedDirectoryEntry> DecodeIsoDirectory(ulong start, uint size)
|
2019-07-19 14:26:02 +01:00
|
|
|
{
|
2019-07-19 16:20:58 +01:00
|
|
|
Dictionary<string, DecodedDirectoryEntry> entries = new Dictionary<string, DecodedDirectoryEntry>();
|
2020-03-10 19:31:34 +00:00
|
|
|
int entryOff = 0;
|
2019-07-19 14:26:02 +01:00
|
|
|
|
2020-11-07 01:17:06 +00:00
|
|
|
byte[] data = ReadSingleExtent(size, (uint)start);
|
2020-03-10 19:31:34 +00:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
while(entryOff + _directoryRecordSize < data.Length)
|
2019-07-19 14:26:02 +01:00
|
|
|
{
|
2020-03-10 19:31:34 +00:00
|
|
|
DirectoryRecord record =
|
2020-07-20 21:11:32 +01:00
|
|
|
Marshal.ByteArrayToStructureLittleEndian<DirectoryRecord>(data, entryOff, _directoryRecordSize);
|
2019-07-31 22:21:03 +01:00
|
|
|
|
2020-03-10 19:31:34 +00:00
|
|
|
if(record.length == 0)
|
2021-04-07 21:54:33 +01:00
|
|
|
{
|
|
|
|
|
// Skip to next sector
|
|
|
|
|
if(data.Length - entryOff >= 2048)
|
|
|
|
|
{
|
|
|
|
|
entryOff = ((data.Length % 2048) + 1) * 2048;
|
|
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-10 19:31:34 +00:00
|
|
|
break;
|
2021-04-07 21:54:33 +01:00
|
|
|
}
|
2019-07-19 14:26:02 +01:00
|
|
|
|
2020-03-10 19:31:34 +00:00
|
|
|
// Special entries for current and parent directories, skip them
|
|
|
|
|
if(record.name_len == 1)
|
2020-07-20 21:11:32 +01:00
|
|
|
if(data[entryOff + _directoryRecordSize] == 0 ||
|
|
|
|
|
data[entryOff + _directoryRecordSize] == 1)
|
2020-03-10 19:31:34 +00:00
|
|
|
{
|
|
|
|
|
entryOff += record.length;
|
2019-07-19 14:26:02 +01:00
|
|
|
|
2020-03-10 19:31:34 +00:00
|
|
|
continue;
|
|
|
|
|
}
|
2020-02-19 01:20:23 +00:00
|
|
|
|
2020-03-10 19:31:34 +00:00
|
|
|
var entry = new DecodedDirectoryEntry
|
|
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
Size = record.size,
|
|
|
|
|
Flags = record.flags,
|
2020-03-10 19:31:34 +00:00
|
|
|
Filename =
|
2020-07-20 21:11:32 +01:00
|
|
|
_joliet ? Encoding.BigEndianUnicode.GetString(data, entryOff + _directoryRecordSize,
|
|
|
|
|
record.name_len)
|
|
|
|
|
: Encoding.GetString(data, entryOff + _directoryRecordSize, record.name_len),
|
2020-07-20 04:34:16 +01:00
|
|
|
FileUnitSize = record.file_unit_size,
|
|
|
|
|
Interleave = record.interleave,
|
|
|
|
|
VolumeSequenceNumber = record.volume_sequence_number,
|
|
|
|
|
Timestamp = DecodeIsoDateTime(record.date),
|
2020-03-10 19:31:34 +00:00
|
|
|
XattrLength = record.xattr_len
|
|
|
|
|
};
|
2019-07-31 22:21:03 +01:00
|
|
|
|
2020-03-10 19:31:34 +00:00
|
|
|
if(record.size != 0)
|
|
|
|
|
entry.Extents = new List<(uint extent, uint size)>
|
2019-07-31 22:21:03 +01:00
|
|
|
{
|
2020-03-10 19:31:34 +00:00
|
|
|
(record.extent, record.size)
|
2019-07-31 22:21:03 +01:00
|
|
|
};
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(entry.Flags.HasFlag(FileFlags.Directory) && _usePathTable)
|
2020-03-10 19:31:34 +00:00
|
|
|
{
|
|
|
|
|
entryOff += record.length;
|
2020-02-19 01:20:23 +00:00
|
|
|
|
2020-03-10 19:31:34 +00:00
|
|
|
continue;
|
|
|
|
|
}
|
2019-07-19 14:26:02 +01:00
|
|
|
|
2020-03-10 19:31:34 +00:00
|
|
|
// Mac OS can use slashes, we cannot
|
|
|
|
|
entry.Filename = entry.Filename.Replace('/', '\u2215');
|
2019-07-29 04:00:51 +01:00
|
|
|
|
2020-03-10 19:31:34 +00:00
|
|
|
// Tailing '.' is only allowed on RRIP. If present it will be recreated below with the alternate name
|
|
|
|
|
if(entry.Filename.EndsWith(".", StringComparison.Ordinal))
|
|
|
|
|
entry.Filename = entry.Filename.Substring(0, entry.Filename.Length - 1);
|
2019-07-29 05:11:36 +01:00
|
|
|
|
2020-03-10 19:31:34 +00:00
|
|
|
if(entry.Filename.EndsWith(".;1", StringComparison.Ordinal))
|
|
|
|
|
entry.Filename = entry.Filename.Substring(0, entry.Filename.Length - 3) + ";1";
|
2019-07-29 05:11:36 +01:00
|
|
|
|
2020-03-10 19:31:34 +00:00
|
|
|
// This is a legal Joliet name, different from VMS version fields, but Nero MAX incorrectly creates these filenames
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_joliet && entry.Filename.EndsWith(";1", StringComparison.Ordinal))
|
2020-03-10 19:31:34 +00:00
|
|
|
entry.Filename = entry.Filename.Substring(0, entry.Filename.Length - 2);
|
2019-07-29 05:11:36 +01:00
|
|
|
|
2020-11-07 01:17:06 +00:00
|
|
|
int systemAreaStart = entryOff + record.name_len + _directoryRecordSize;
|
2020-07-20 21:11:32 +01:00
|
|
|
int systemAreaLength = record.length - record.name_len - _directoryRecordSize;
|
2019-07-29 05:11:36 +01:00
|
|
|
|
2020-03-10 19:31:34 +00:00
|
|
|
if(systemAreaStart % 2 != 0)
|
|
|
|
|
{
|
|
|
|
|
systemAreaStart++;
|
|
|
|
|
systemAreaLength--;
|
|
|
|
|
}
|
2019-07-28 14:03:38 +01:00
|
|
|
|
2020-03-10 19:31:34 +00:00
|
|
|
DecodeSystemArea(data, systemAreaStart, systemAreaStart + systemAreaLength, ref entry,
|
|
|
|
|
out bool hasResourceFork);
|
2019-07-28 05:20:57 +01:00
|
|
|
|
2020-03-10 19:31:34 +00:00
|
|
|
if(entry.Flags.HasFlag(FileFlags.Associated))
|
|
|
|
|
{
|
|
|
|
|
if(entries.ContainsKey(entry.Filename))
|
2019-07-28 05:20:57 +01:00
|
|
|
{
|
2020-03-10 19:31:34 +00:00
|
|
|
if(hasResourceFork)
|
2019-07-31 19:39:55 +01:00
|
|
|
{
|
2020-03-10 19:31:34 +00:00
|
|
|
entries[entry.Filename].ResourceFork.Size += entry.Size;
|
|
|
|
|
entries[entry.Filename].ResourceFork.Extents.Add(entry.Extents[0]);
|
2019-07-31 19:39:55 +01:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2020-03-10 19:31:34 +00:00
|
|
|
entries[entry.Filename].AssociatedFile.Size += entry.Size;
|
|
|
|
|
entries[entry.Filename].AssociatedFile.Extents.Add(entry.Extents[0]);
|
2019-07-31 19:39:55 +01:00
|
|
|
}
|
2019-07-28 05:20:57 +01:00
|
|
|
}
|
2019-07-22 01:08:05 +01:00
|
|
|
else
|
2019-07-28 13:56:22 +01:00
|
|
|
{
|
2020-03-10 19:31:34 +00:00
|
|
|
entries[entry.Filename] = new DecodedDirectoryEntry
|
2019-07-22 01:08:05 +01:00
|
|
|
{
|
2020-03-10 19:31:34 +00:00
|
|
|
Size = 0,
|
|
|
|
|
Flags = record.flags ^ FileFlags.Associated,
|
2020-07-20 04:34:16 +01:00
|
|
|
FileUnitSize = 0,
|
|
|
|
|
Interleave = 0,
|
|
|
|
|
VolumeSequenceNumber = record.volume_sequence_number,
|
|
|
|
|
Filename = entry.Filename,
|
|
|
|
|
Timestamp = DecodeIsoDateTime(record.date),
|
|
|
|
|
XattrLength = 0
|
2020-03-10 19:31:34 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if(hasResourceFork)
|
|
|
|
|
entries[entry.Filename].ResourceFork = entry;
|
2020-02-19 01:20:23 +00:00
|
|
|
else
|
2020-03-10 19:31:34 +00:00
|
|
|
entries[entry.Filename].AssociatedFile = entry;
|
2019-07-31 19:39:55 +01:00
|
|
|
}
|
2020-03-10 19:31:34 +00:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if(entries.ContainsKey(entry.Filename))
|
|
|
|
|
{
|
|
|
|
|
entries[entry.Filename].Size += entry.Size;
|
2019-07-31 22:21:03 +01:00
|
|
|
|
2020-03-10 19:31:34 +00:00
|
|
|
// Can appear after an associated file
|
|
|
|
|
if(entries[entry.Filename].Extents is null)
|
|
|
|
|
{
|
|
|
|
|
entries[entry.Filename].Extents = new List<(uint extent, uint size)>();
|
|
|
|
|
entries[entry.Filename].Flags = entry.Flags;
|
|
|
|
|
entries[entry.Filename].FileUnitSize = entry.FileUnitSize;
|
|
|
|
|
entries[entry.Filename].Interleave = entry.Interleave;
|
|
|
|
|
entries[entry.Filename].VolumeSequenceNumber = entry.VolumeSequenceNumber;
|
|
|
|
|
entries[entry.Filename].Filename = entry.Filename;
|
|
|
|
|
entries[entry.Filename].Timestamp = entry.Timestamp;
|
|
|
|
|
entries[entry.Filename].XattrLength = entry.XattrLength;
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-18 20:49:46 +01:00
|
|
|
if(entry.Extents?.Count > 0)
|
|
|
|
|
entries[entry.Filename].Extents.Add(entry.Extents[0]);
|
2020-03-10 19:31:34 +00:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
entries[entry.Filename] = entry;
|
2019-07-22 01:08:05 +01:00
|
|
|
}
|
2019-07-19 14:26:02 +01:00
|
|
|
|
2020-03-10 19:31:34 +00:00
|
|
|
entryOff += record.length;
|
2019-07-19 14:26:02 +01:00
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_useTransTbl)
|
2020-02-19 01:20:23 +00:00
|
|
|
DecodeTransTable(entries);
|
2019-07-31 01:02:56 +01:00
|
|
|
|
2019-07-29 14:04:30 +01:00
|
|
|
// Relocated directories should be shown in correct place when using Rock Ridge namespace
|
2020-07-20 21:11:32 +01:00
|
|
|
return _namespace == Namespace.Rrip
|
2019-07-29 14:04:30 +01:00
|
|
|
? entries.Where(e => !e.Value.RockRidgeRelocated).ToDictionary(x => x.Key, x => x.Value)
|
|
|
|
|
: entries;
|
2019-07-19 14:26:02 +01:00
|
|
|
}
|
2019-07-28 16:55:15 +01:00
|
|
|
|
2019-07-31 01:02:56 +01:00
|
|
|
void DecodeTransTable(Dictionary<string, DecodedDirectoryEntry> entries)
|
|
|
|
|
{
|
|
|
|
|
KeyValuePair<string, DecodedDirectoryEntry> transTblEntry =
|
|
|
|
|
entries.FirstOrDefault(e => !e.Value.Flags.HasFlag(FileFlags.Directory) &&
|
|
|
|
|
(e.Value.Filename.ToLower(CultureInfo.CurrentUICulture) == "trans.tbl" ||
|
|
|
|
|
e.Value.Filename.ToLower(CultureInfo.CurrentUICulture) == "trans.tbl;1"));
|
|
|
|
|
|
2020-02-19 01:20:23 +00:00
|
|
|
if(transTblEntry.Value == null)
|
|
|
|
|
return;
|
2019-07-31 01:02:56 +01:00
|
|
|
|
2020-03-10 22:47:10 +00:00
|
|
|
byte[] transTbl = ReadWithExtents(0, (long)transTblEntry.Value.Size, transTblEntry.Value.Extents,
|
|
|
|
|
transTblEntry.Value.XA?.signature == XA_MAGIC &&
|
|
|
|
|
transTblEntry.Value.XA?.attributes.HasFlag(XaAttributes.Interleaved) ==
|
|
|
|
|
true, transTblEntry.Value.XA?.filenumber ?? 0);
|
2019-07-31 01:02:56 +01:00
|
|
|
|
2020-02-19 01:20:23 +00:00
|
|
|
var mr = new MemoryStream(transTbl, 0, (int)transTblEntry.Value.Size, false);
|
|
|
|
|
var sr = new StreamReader(mr, Encoding);
|
2019-07-31 01:02:56 +01:00
|
|
|
|
|
|
|
|
string line = sr.ReadLine();
|
|
|
|
|
|
|
|
|
|
while(line != null)
|
|
|
|
|
{
|
|
|
|
|
// Skip the type field and the first space
|
|
|
|
|
string cutLine = line.Substring(2);
|
|
|
|
|
int spaceIndex = cutLine.IndexOf(' ');
|
|
|
|
|
string originalName = cutLine.Substring(0, spaceIndex);
|
|
|
|
|
string originalNameWithVersion;
|
2019-07-31 15:49:07 +01:00
|
|
|
string newName = cutLine.Substring(spaceIndex + 1).TrimStart();
|
2019-07-31 01:02:56 +01:00
|
|
|
|
|
|
|
|
if(originalName.EndsWith(";1", StringComparison.Ordinal))
|
|
|
|
|
{
|
|
|
|
|
originalNameWithVersion = originalName.ToLower(CultureInfo.CurrentUICulture);
|
|
|
|
|
originalName = originalNameWithVersion.Substring(0, originalName.Length - 2);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
originalName = originalName.ToLower(CultureInfo.CurrentUICulture);
|
|
|
|
|
originalNameWithVersion = originalName + ";1";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Pre-read next line
|
|
|
|
|
line = sr.ReadLine();
|
|
|
|
|
|
|
|
|
|
KeyValuePair<string, DecodedDirectoryEntry> originalEntry =
|
|
|
|
|
entries.FirstOrDefault(e => !e.Value.Flags.HasFlag(FileFlags.Directory) &&
|
|
|
|
|
(e.Value.Filename.ToLower(CultureInfo.CurrentUICulture) ==
|
|
|
|
|
originalName ||
|
|
|
|
|
e.Value.Filename.ToLower(CultureInfo.CurrentUICulture) ==
|
|
|
|
|
originalNameWithVersion));
|
|
|
|
|
|
|
|
|
|
originalEntry.Value.Filename = newName;
|
|
|
|
|
entries.Remove(originalEntry.Key);
|
|
|
|
|
entries[newName] = originalEntry.Value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
entries.Remove(transTblEntry.Key);
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-19 01:20:23 +00:00
|
|
|
void DecodeSystemArea(byte[] data, int start, int end, ref DecodedDirectoryEntry entry,
|
2019-07-28 18:19:17 +01:00
|
|
|
out bool hasResourceFork)
|
2019-07-28 16:55:15 +01:00
|
|
|
{
|
|
|
|
|
int systemAreaOff = start;
|
|
|
|
|
hasResourceFork = false;
|
2020-02-19 03:37:51 +00:00
|
|
|
bool continueSymlink = false;
|
|
|
|
|
bool continueSymlinkComponent = false;
|
|
|
|
|
AppleCommon.FInfo fInfo;
|
2019-07-28 16:55:15 +01:00
|
|
|
|
|
|
|
|
while(systemAreaOff + 2 <= end)
|
|
|
|
|
{
|
|
|
|
|
ushort systemAreaSignature = BigEndianBitConverter.ToUInt16(data, systemAreaOff);
|
|
|
|
|
|
2020-02-19 01:20:23 +00:00
|
|
|
if(BigEndianBitConverter.ToUInt16(data, systemAreaOff + 6) == XA_MAGIC)
|
|
|
|
|
systemAreaSignature = XA_MAGIC;
|
2019-07-28 16:55:15 +01:00
|
|
|
|
|
|
|
|
switch(systemAreaSignature)
|
|
|
|
|
{
|
|
|
|
|
case APPLE_MAGIC:
|
2020-02-19 01:20:23 +00:00
|
|
|
byte appleLength = data[systemAreaOff + 2];
|
|
|
|
|
var appleId = (AppleId)data[systemAreaOff + 3];
|
2019-07-28 16:55:15 +01:00
|
|
|
|
2019-07-28 17:04:08 +01:00
|
|
|
// Old AAIP
|
2020-02-19 01:20:23 +00:00
|
|
|
if(appleId == AppleId.ProDOS &&
|
|
|
|
|
appleLength != 7)
|
|
|
|
|
goto case AAIP_MAGIC;
|
2019-07-28 17:04:08 +01:00
|
|
|
|
2019-07-28 16:55:15 +01:00
|
|
|
switch(appleId)
|
|
|
|
|
{
|
|
|
|
|
case AppleId.ProDOS:
|
|
|
|
|
AppleProDOSSystemUse appleProDosSystemUse =
|
|
|
|
|
Marshal.ByteArrayToStructureLittleEndian<AppleProDOSSystemUse>(data, systemAreaOff,
|
2020-11-07 01:17:06 +00:00
|
|
|
Marshal.SizeOf<AppleProDOSSystemUse>());
|
2019-07-28 16:55:15 +01:00
|
|
|
|
|
|
|
|
entry.AppleProDosType = appleProDosSystemUse.aux_type;
|
|
|
|
|
entry.AppleDosType = appleProDosSystemUse.type;
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
case AppleId.HFS:
|
|
|
|
|
AppleHFSSystemUse appleHfsSystemUse =
|
|
|
|
|
Marshal.ByteArrayToStructureBigEndian<AppleHFSSystemUse>(data, systemAreaOff,
|
2020-11-07 01:17:06 +00:00
|
|
|
Marshal.SizeOf<AppleHFSSystemUse>());
|
2019-07-28 16:55:15 +01:00
|
|
|
|
|
|
|
|
hasResourceFork = true;
|
|
|
|
|
|
2020-02-19 03:37:51 +00:00
|
|
|
fInfo = new AppleCommon.FInfo();
|
|
|
|
|
fInfo.fdCreator = appleHfsSystemUse.creator;
|
|
|
|
|
fInfo.fdFlags = appleHfsSystemUse.finder_flags;
|
|
|
|
|
fInfo.fdType = appleHfsSystemUse.type;
|
|
|
|
|
entry.FinderInfo = fInfo;
|
2019-07-28 16:55:15 +01:00
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
systemAreaOff += appleLength;
|
2020-02-19 01:20:23 +00:00
|
|
|
|
2019-07-28 16:55:15 +01:00
|
|
|
break;
|
|
|
|
|
case APPLE_MAGIC_OLD:
|
2020-02-19 01:20:23 +00:00
|
|
|
var appleOldId = (AppleOldId)data[systemAreaOff + 2];
|
2019-07-28 16:55:15 +01:00
|
|
|
|
|
|
|
|
switch(appleOldId)
|
|
|
|
|
{
|
|
|
|
|
case AppleOldId.ProDOS:
|
|
|
|
|
AppleProDOSOldSystemUse appleProDosOldSystemUse =
|
|
|
|
|
Marshal.ByteArrayToStructureLittleEndian<AppleProDOSOldSystemUse>(data,
|
2020-11-07 01:17:06 +00:00
|
|
|
systemAreaOff, Marshal.SizeOf<AppleProDOSOldSystemUse>());
|
2020-02-19 01:20:23 +00:00
|
|
|
|
2019-07-28 16:55:15 +01:00
|
|
|
entry.AppleProDosType = appleProDosOldSystemUse.aux_type;
|
|
|
|
|
entry.AppleDosType = appleProDosOldSystemUse.type;
|
|
|
|
|
|
|
|
|
|
systemAreaOff += Marshal.SizeOf<AppleProDOSOldSystemUse>();
|
2020-02-19 01:20:23 +00:00
|
|
|
|
2019-07-28 16:55:15 +01:00
|
|
|
break;
|
|
|
|
|
case AppleOldId.TypeCreator:
|
|
|
|
|
case AppleOldId.TypeCreatorBundle:
|
|
|
|
|
AppleHFSTypeCreatorSystemUse appleHfsTypeCreatorSystemUse =
|
|
|
|
|
Marshal.ByteArrayToStructureBigEndian<AppleHFSTypeCreatorSystemUse>(data,
|
2020-11-07 01:17:06 +00:00
|
|
|
systemAreaOff, Marshal.SizeOf<AppleHFSTypeCreatorSystemUse>());
|
2019-07-28 16:55:15 +01:00
|
|
|
|
|
|
|
|
hasResourceFork = true;
|
|
|
|
|
|
2020-02-19 03:37:51 +00:00
|
|
|
fInfo = new AppleCommon.FInfo();
|
|
|
|
|
fInfo.fdCreator = appleHfsTypeCreatorSystemUse.creator;
|
|
|
|
|
fInfo.fdType = appleHfsTypeCreatorSystemUse.type;
|
|
|
|
|
entry.FinderInfo = fInfo;
|
2019-07-28 16:55:15 +01:00
|
|
|
|
|
|
|
|
systemAreaOff += Marshal.SizeOf<AppleHFSTypeCreatorSystemUse>();
|
2020-02-19 01:20:23 +00:00
|
|
|
|
2019-07-28 16:55:15 +01:00
|
|
|
break;
|
|
|
|
|
case AppleOldId.TypeCreatorIcon:
|
|
|
|
|
case AppleOldId.TypeCreatorIconBundle:
|
|
|
|
|
AppleHFSIconSystemUse appleHfsIconSystemUse =
|
|
|
|
|
Marshal.ByteArrayToStructureBigEndian<AppleHFSIconSystemUse>(data, systemAreaOff,
|
2020-11-07 01:17:06 +00:00
|
|
|
Marshal.SizeOf<AppleHFSIconSystemUse>());
|
2019-07-28 16:55:15 +01:00
|
|
|
|
|
|
|
|
hasResourceFork = true;
|
|
|
|
|
|
2020-02-19 03:37:51 +00:00
|
|
|
fInfo = new AppleCommon.FInfo();
|
|
|
|
|
fInfo.fdCreator = appleHfsIconSystemUse.creator;
|
|
|
|
|
fInfo.fdType = appleHfsIconSystemUse.type;
|
|
|
|
|
entry.FinderInfo = fInfo;
|
|
|
|
|
entry.AppleIcon = appleHfsIconSystemUse.icon;
|
2019-07-28 16:55:15 +01:00
|
|
|
|
|
|
|
|
systemAreaOff += Marshal.SizeOf<AppleHFSIconSystemUse>();
|
2020-02-19 01:20:23 +00:00
|
|
|
|
2019-07-28 16:55:15 +01:00
|
|
|
break;
|
|
|
|
|
case AppleOldId.HFS:
|
|
|
|
|
AppleHFSOldSystemUse appleHfsSystemUse =
|
|
|
|
|
Marshal.ByteArrayToStructureBigEndian<AppleHFSOldSystemUse>(data, systemAreaOff,
|
2020-11-07 01:17:06 +00:00
|
|
|
Marshal.SizeOf<AppleHFSOldSystemUse>());
|
2019-07-28 16:55:15 +01:00
|
|
|
|
|
|
|
|
hasResourceFork = true;
|
|
|
|
|
|
2020-02-19 03:37:51 +00:00
|
|
|
fInfo = new AppleCommon.FInfo();
|
|
|
|
|
fInfo.fdCreator = appleHfsSystemUse.creator;
|
|
|
|
|
fInfo.fdFlags = (AppleCommon.FinderFlags)appleHfsSystemUse.finder_flags;
|
|
|
|
|
fInfo.fdType = appleHfsSystemUse.type;
|
|
|
|
|
entry.FinderInfo = fInfo;
|
2019-07-28 16:55:15 +01:00
|
|
|
|
|
|
|
|
systemAreaOff += Marshal.SizeOf<AppleHFSOldSystemUse>();
|
2020-02-19 01:20:23 +00:00
|
|
|
|
2019-07-28 16:55:15 +01:00
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
// Cannot continue as we don't know this structure size
|
|
|
|
|
systemAreaOff = end;
|
2020-02-19 01:20:23 +00:00
|
|
|
|
2019-07-28 16:55:15 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
case XA_MAGIC:
|
|
|
|
|
entry.XA = Marshal.ByteArrayToStructureBigEndian<CdromXa>(data, systemAreaOff,
|
2020-11-07 01:17:06 +00:00
|
|
|
Marshal.SizeOf<CdromXa>());
|
2019-07-28 16:55:15 +01:00
|
|
|
|
|
|
|
|
systemAreaOff += Marshal.SizeOf<CdromXa>();
|
2020-02-19 01:20:23 +00:00
|
|
|
|
2019-07-28 17:04:08 +01:00
|
|
|
break;
|
2020-02-19 01:20:23 +00:00
|
|
|
|
2019-07-28 17:04:08 +01:00
|
|
|
// All of these follow the SUSP indication of 2 bytes for signature 1 byte for length
|
|
|
|
|
case AAIP_MAGIC:
|
|
|
|
|
case AMIGA_MAGIC:
|
2019-07-28 17:29:45 +01:00
|
|
|
AmigaEntry amiga =
|
|
|
|
|
Marshal.ByteArrayToStructureBigEndian<AmigaEntry>(data, systemAreaOff,
|
|
|
|
|
Marshal.SizeOf<AmigaEntry>());
|
|
|
|
|
|
|
|
|
|
int protectionLength = 0;
|
|
|
|
|
|
|
|
|
|
if(amiga.flags.HasFlag(AmigaFlags.Protection))
|
|
|
|
|
{
|
|
|
|
|
entry.AmigaProtection =
|
|
|
|
|
Marshal.ByteArrayToStructureBigEndian<AmigaProtection>(data,
|
2020-11-07 01:17:06 +00:00
|
|
|
systemAreaOff + Marshal.SizeOf<AmigaEntry>(),
|
|
|
|
|
Marshal.SizeOf<AmigaProtection>());
|
2019-07-28 17:29:45 +01:00
|
|
|
|
|
|
|
|
protectionLength = Marshal.SizeOf<AmigaProtection>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(amiga.flags.HasFlag(AmigaFlags.Comment))
|
|
|
|
|
{
|
2020-02-19 01:20:23 +00:00
|
|
|
if(entry.AmigaComment is null)
|
|
|
|
|
entry.AmigaComment = new byte[0];
|
2019-07-28 17:29:45 +01:00
|
|
|
|
2021-04-07 21:54:33 +01:00
|
|
|
byte[] newComment = new byte[entry.AmigaComment.Length +
|
|
|
|
|
data
|
|
|
|
|
[systemAreaOff + Marshal.SizeOf<AmigaEntry>() + protectionLength] -
|
|
|
|
|
1];
|
2019-07-28 17:29:45 +01:00
|
|
|
|
|
|
|
|
Array.Copy(entry.AmigaComment, 0, newComment, 0, entry.AmigaComment.Length);
|
|
|
|
|
|
|
|
|
|
Array.Copy(data, systemAreaOff + Marshal.SizeOf<AmigaEntry>() + protectionLength,
|
|
|
|
|
newComment, entry.AmigaComment.Length,
|
|
|
|
|
data[systemAreaOff + Marshal.SizeOf<AmigaEntry>() + protectionLength] - 1);
|
|
|
|
|
|
|
|
|
|
entry.AmigaComment = newComment;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
systemAreaOff += amiga.length;
|
2020-02-19 01:20:23 +00:00
|
|
|
|
2019-07-28 17:29:45 +01:00
|
|
|
break;
|
2020-02-19 01:20:23 +00:00
|
|
|
|
2019-07-28 17:31:30 +01:00
|
|
|
// This merely indicates the existence of RRIP extensions, we don't need it
|
2019-07-28 17:04:08 +01:00
|
|
|
case RRIP_MAGIC:
|
2019-07-28 17:31:30 +01:00
|
|
|
byte rripLength = data[systemAreaOff + 2];
|
|
|
|
|
systemAreaOff += rripLength;
|
|
|
|
|
|
|
|
|
|
break;
|
2019-07-28 17:04:08 +01:00
|
|
|
case RRIP_POSIX_ATTRIBUTES:
|
2019-07-28 17:46:09 +01:00
|
|
|
byte pxLength = data[systemAreaOff + 2];
|
|
|
|
|
|
|
|
|
|
if(pxLength == 36)
|
|
|
|
|
entry.PosixAttributesOld =
|
|
|
|
|
Marshal.ByteArrayToStructureLittleEndian<PosixAttributesOld>(data, systemAreaOff,
|
2020-11-07 01:17:06 +00:00
|
|
|
Marshal.SizeOf<PosixAttributesOld>());
|
2019-07-28 17:46:09 +01:00
|
|
|
else if(pxLength >= 44)
|
|
|
|
|
entry.PosixAttributes =
|
|
|
|
|
Marshal.ByteArrayToStructureLittleEndian<PosixAttributes>(data, systemAreaOff,
|
2020-11-07 01:17:06 +00:00
|
|
|
Marshal.SizeOf<PosixAttributes>());
|
2019-07-28 17:46:09 +01:00
|
|
|
|
|
|
|
|
systemAreaOff += pxLength;
|
2020-02-19 01:20:23 +00:00
|
|
|
|
2019-07-28 17:46:09 +01:00
|
|
|
break;
|
2019-07-28 17:04:08 +01:00
|
|
|
case RRIP_POSIX_DEV_NO:
|
2019-07-28 17:54:40 +01:00
|
|
|
byte pnLength = data[systemAreaOff + 2];
|
|
|
|
|
|
|
|
|
|
entry.PosixDeviceNumber =
|
|
|
|
|
Marshal.ByteArrayToStructureLittleEndian<PosixDeviceNumber>(data, systemAreaOff,
|
2020-11-07 01:17:06 +00:00
|
|
|
Marshal.SizeOf<PosixDeviceNumber>());
|
2020-02-19 01:20:23 +00:00
|
|
|
|
2019-07-28 17:54:40 +01:00
|
|
|
systemAreaOff += pnLength;
|
2020-02-19 01:20:23 +00:00
|
|
|
|
2019-07-28 17:54:40 +01:00
|
|
|
break;
|
2019-07-28 17:04:08 +01:00
|
|
|
case RRIP_SYMLINK:
|
2019-07-28 17:55:41 +01:00
|
|
|
byte slLength = data[systemAreaOff + 2];
|
|
|
|
|
|
2019-07-28 21:33:05 +01:00
|
|
|
SymbolicLink sl =
|
|
|
|
|
Marshal.ByteArrayToStructureLittleEndian<SymbolicLink>(data, systemAreaOff,
|
2020-11-07 01:17:06 +00:00
|
|
|
Marshal.SizeOf<SymbolicLink>());
|
2019-07-28 21:33:05 +01:00
|
|
|
|
|
|
|
|
SymbolicLinkComponent slc =
|
|
|
|
|
Marshal.ByteArrayToStructureLittleEndian<SymbolicLinkComponent>(data,
|
2020-11-07 01:17:06 +00:00
|
|
|
systemAreaOff + Marshal.SizeOf<SymbolicLink>(),
|
|
|
|
|
Marshal.SizeOf<SymbolicLinkComponent>());
|
2019-07-28 21:33:05 +01:00
|
|
|
|
2020-02-19 01:20:23 +00:00
|
|
|
if(!continueSymlink ||
|
|
|
|
|
entry.SymbolicLink is null)
|
|
|
|
|
entry.SymbolicLink = "";
|
|
|
|
|
|
|
|
|
|
if(slc.flags.HasFlag(SymlinkComponentFlags.Root))
|
|
|
|
|
entry.SymbolicLink = "/";
|
|
|
|
|
|
|
|
|
|
if(slc.flags.HasFlag(SymlinkComponentFlags.Current))
|
|
|
|
|
entry.SymbolicLink += ".";
|
2019-07-28 21:33:05 +01:00
|
|
|
|
2020-02-19 01:20:23 +00:00
|
|
|
if(slc.flags.HasFlag(SymlinkComponentFlags.Parent))
|
|
|
|
|
entry.SymbolicLink += "..";
|
2019-07-28 21:33:05 +01:00
|
|
|
|
2020-02-19 01:20:23 +00:00
|
|
|
if(!continueSymlinkComponent &&
|
|
|
|
|
!slc.flags.HasFlag(SymlinkComponentFlags.Root))
|
2019-07-28 21:33:05 +01:00
|
|
|
entry.SymbolicLink += "/";
|
|
|
|
|
|
|
|
|
|
entry.SymbolicLink += slc.flags.HasFlag(SymlinkComponentFlags.Networkname)
|
|
|
|
|
? Environment.MachineName
|
2020-07-20 21:11:32 +01:00
|
|
|
: _joliet
|
2019-07-28 21:33:05 +01:00
|
|
|
? Encoding.BigEndianUnicode.GetString(data,
|
2020-11-07 01:17:06 +00:00
|
|
|
systemAreaOff + Marshal.SizeOf<SymbolicLink>() +
|
|
|
|
|
Marshal.SizeOf<SymbolicLinkComponent>(), slc.length)
|
2019-07-28 21:33:05 +01:00
|
|
|
: Encoding.GetString(data,
|
|
|
|
|
systemAreaOff +
|
|
|
|
|
Marshal.SizeOf<SymbolicLink>() +
|
|
|
|
|
Marshal.SizeOf<SymbolicLinkComponent>(),
|
|
|
|
|
slc.length);
|
|
|
|
|
|
2019-07-29 04:14:49 +01:00
|
|
|
continueSymlink = sl.flags.HasFlag(SymlinkFlags.Continue);
|
|
|
|
|
continueSymlinkComponent = slc.flags.HasFlag(SymlinkComponentFlags.Continue);
|
2019-07-28 21:33:05 +01:00
|
|
|
|
|
|
|
|
systemAreaOff += slLength;
|
2020-02-19 01:20:23 +00:00
|
|
|
|
2019-07-28 17:55:41 +01:00
|
|
|
break;
|
2019-07-28 17:04:08 +01:00
|
|
|
case RRIP_NAME:
|
2019-07-28 18:19:17 +01:00
|
|
|
byte nmLength = data[systemAreaOff + 2];
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_namespace != Namespace.Rrip)
|
2019-07-28 18:19:17 +01:00
|
|
|
{
|
|
|
|
|
systemAreaOff += nmLength;
|
2020-02-19 01:20:23 +00:00
|
|
|
|
2019-07-28 18:19:17 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AlternateName alternateName =
|
|
|
|
|
Marshal.ByteArrayToStructureLittleEndian<AlternateName>(data, systemAreaOff,
|
2020-11-07 01:17:06 +00:00
|
|
|
Marshal.SizeOf<AlternateName>());
|
2019-07-28 18:19:17 +01:00
|
|
|
|
|
|
|
|
byte[] nm;
|
2020-02-19 01:20:23 +00:00
|
|
|
|
2019-07-28 18:19:17 +01:00
|
|
|
if(alternateName.flags.HasFlag(AlternateNameFlags.Networkname))
|
2020-07-20 21:11:32 +01:00
|
|
|
nm = _joliet ? Encoding.BigEndianUnicode.GetBytes(Environment.MachineName)
|
2019-07-28 18:19:17 +01:00
|
|
|
: Encoding.GetBytes(Environment.MachineName);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
nm = new byte[nmLength - Marshal.SizeOf<AlternateName>()];
|
|
|
|
|
|
|
|
|
|
Array.Copy(data, systemAreaOff + Marshal.SizeOf<AlternateName>(), nm, 0, nm.Length);
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-19 01:20:23 +00:00
|
|
|
if(entry.RockRidgeAlternateName is null)
|
|
|
|
|
entry.RockRidgeAlternateName = new byte[0];
|
2019-07-28 18:19:17 +01:00
|
|
|
|
|
|
|
|
byte[] newNm = new byte[entry.RockRidgeAlternateName.Length + nm.Length];
|
2020-02-19 01:20:23 +00:00
|
|
|
Array.Copy(entry.RockRidgeAlternateName, 0, newNm, 0, entry.RockRidgeAlternateName.Length);
|
|
|
|
|
Array.Copy(nm, 0, newNm, entry.RockRidgeAlternateName.Length, nm.Length);
|
2019-07-28 18:19:17 +01:00
|
|
|
|
|
|
|
|
entry.RockRidgeAlternateName = newNm;
|
|
|
|
|
|
|
|
|
|
if(!alternateName.flags.HasFlag(AlternateNameFlags.Continue))
|
|
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
entry.Filename = _joliet ? Encoding.BigEndianUnicode.GetString(entry.RockRidgeAlternateName)
|
2019-07-28 18:19:17 +01:00
|
|
|
: Encoding.GetString(entry.RockRidgeAlternateName);
|
2020-02-19 01:20:23 +00:00
|
|
|
|
2019-07-28 18:19:17 +01:00
|
|
|
entry.RockRidgeAlternateName = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
systemAreaOff += nmLength;
|
2020-02-19 01:20:23 +00:00
|
|
|
|
2019-07-28 18:19:17 +01:00
|
|
|
break;
|
2019-07-28 17:04:08 +01:00
|
|
|
case RRIP_CHILDLINK:
|
2019-07-28 18:19:44 +01:00
|
|
|
byte clLength = data[systemAreaOff + 2];
|
2019-07-29 14:04:30 +01:00
|
|
|
|
|
|
|
|
// If we are not in Rock Ridge namespace, or we are using the Path Table, skip it
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_namespace != Namespace.Rrip || _usePathTable)
|
2019-07-29 14:04:30 +01:00
|
|
|
{
|
|
|
|
|
systemAreaOff += clLength;
|
2020-02-19 01:20:23 +00:00
|
|
|
|
2019-07-29 14:04:30 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ChildLink cl =
|
|
|
|
|
Marshal.ByteArrayToStructureLittleEndian<ChildLink>(data, systemAreaOff,
|
2020-11-07 01:17:06 +00:00
|
|
|
Marshal.SizeOf<ChildLink>());
|
2019-07-29 14:04:30 +01:00
|
|
|
|
2020-03-10 19:31:34 +00:00
|
|
|
byte[] childSector = ReadSector(cl.child_dir_lba);
|
2020-02-19 01:20:23 +00:00
|
|
|
|
2019-07-29 14:04:30 +01:00
|
|
|
DirectoryRecord childRecord =
|
|
|
|
|
Marshal.ByteArrayToStructureLittleEndian<DirectoryRecord>(childSector);
|
|
|
|
|
|
|
|
|
|
// As per RRIP 4.1.5.1, we leave name as in previous entry, substitute location with the one in
|
|
|
|
|
// the CL, and replace all other fields with the ones found in the first entry of the child
|
2020-02-19 01:20:23 +00:00
|
|
|
entry.Extents = new List<(uint extent, uint size)>
|
|
|
|
|
{
|
|
|
|
|
(cl.child_dir_lba, childRecord.size)
|
|
|
|
|
};
|
|
|
|
|
|
2019-07-29 14:04:30 +01:00
|
|
|
entry.Size = childRecord.size;
|
|
|
|
|
entry.Flags = childRecord.flags;
|
|
|
|
|
entry.FileUnitSize = childRecord.file_unit_size;
|
|
|
|
|
entry.Interleave = childRecord.interleave;
|
|
|
|
|
entry.VolumeSequenceNumber = childRecord.volume_sequence_number;
|
|
|
|
|
entry.Timestamp = DecodeIsoDateTime(childRecord.date);
|
2019-07-31 05:47:28 +01:00
|
|
|
entry.XattrLength = childRecord.xattr_len;
|
2019-07-29 14:04:30 +01:00
|
|
|
|
2019-07-28 18:19:44 +01:00
|
|
|
systemAreaOff += clLength;
|
|
|
|
|
|
|
|
|
|
break;
|
2019-07-28 17:04:08 +01:00
|
|
|
case RRIP_PARENTLINK:
|
2019-07-29 14:04:30 +01:00
|
|
|
// SKip, we don't need it
|
2019-07-28 18:19:44 +01:00
|
|
|
byte plLength = data[systemAreaOff + 2];
|
|
|
|
|
systemAreaOff += plLength;
|
|
|
|
|
|
|
|
|
|
break;
|
2019-07-28 17:04:08 +01:00
|
|
|
case RRIP_RELOCATED_DIR:
|
2019-07-28 18:19:44 +01:00
|
|
|
byte reLength = data[systemAreaOff + 2];
|
|
|
|
|
systemAreaOff += reLength;
|
|
|
|
|
|
2019-07-29 14:04:30 +01:00
|
|
|
entry.RockRidgeRelocated = true;
|
|
|
|
|
|
2019-07-28 18:19:44 +01:00
|
|
|
break;
|
2019-07-28 17:04:08 +01:00
|
|
|
case RRIP_TIMESTAMPS:
|
2019-07-28 18:32:15 +01:00
|
|
|
byte tfLength = data[systemAreaOff + 2];
|
|
|
|
|
|
|
|
|
|
Timestamps timestamps =
|
|
|
|
|
Marshal.ByteArrayToStructureLittleEndian<Timestamps>(data, systemAreaOff,
|
2020-11-07 01:17:06 +00:00
|
|
|
Marshal.SizeOf<Timestamps>());
|
2019-07-28 18:32:15 +01:00
|
|
|
|
|
|
|
|
int tfOff = systemAreaOff + Marshal.SizeOf<Timestamps>();
|
|
|
|
|
int tfLen = timestamps.flags.HasFlag(TimestampFlags.LongFormat) ? 17 : 7;
|
|
|
|
|
|
|
|
|
|
if(timestamps.flags.HasFlag(TimestampFlags.Creation))
|
|
|
|
|
{
|
|
|
|
|
entry.RripCreation = new byte[tfLen];
|
|
|
|
|
Array.Copy(data, tfOff, entry.RripCreation, 0, tfLen);
|
|
|
|
|
tfOff += tfLen;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(timestamps.flags.HasFlag(TimestampFlags.Modification))
|
|
|
|
|
{
|
|
|
|
|
entry.RripModify = new byte[tfLen];
|
|
|
|
|
Array.Copy(data, tfOff, entry.RripModify, 0, tfLen);
|
|
|
|
|
tfOff += tfLen;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(timestamps.flags.HasFlag(TimestampFlags.Access))
|
|
|
|
|
{
|
|
|
|
|
entry.RripAccess = new byte[tfLen];
|
|
|
|
|
Array.Copy(data, tfOff, entry.RripAccess, 0, tfLen);
|
|
|
|
|
tfOff += tfLen;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(timestamps.flags.HasFlag(TimestampFlags.AttributeChange))
|
|
|
|
|
{
|
|
|
|
|
entry.RripAttributeChange = new byte[tfLen];
|
|
|
|
|
Array.Copy(data, tfOff, entry.RripAttributeChange, 0, tfLen);
|
|
|
|
|
tfOff += tfLen;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(timestamps.flags.HasFlag(TimestampFlags.Backup))
|
|
|
|
|
{
|
|
|
|
|
entry.RripBackup = new byte[tfLen];
|
|
|
|
|
Array.Copy(data, tfOff, entry.RripBackup, 0, tfLen);
|
|
|
|
|
tfOff += tfLen;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(timestamps.flags.HasFlag(TimestampFlags.Expiration))
|
|
|
|
|
{
|
|
|
|
|
entry.RripExpiration = new byte[tfLen];
|
|
|
|
|
Array.Copy(data, tfOff, entry.RripExpiration, 0, tfLen);
|
|
|
|
|
tfOff += tfLen;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(timestamps.flags.HasFlag(TimestampFlags.Effective))
|
|
|
|
|
{
|
|
|
|
|
entry.RripEffective = new byte[tfLen];
|
|
|
|
|
Array.Copy(data, tfOff, entry.RripEffective, 0, tfLen);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
systemAreaOff += tfLength;
|
2020-02-19 01:20:23 +00:00
|
|
|
|
2019-07-28 18:32:15 +01:00
|
|
|
break;
|
2019-07-28 17:04:08 +01:00
|
|
|
case RRIP_SPARSE:
|
2019-07-28 18:32:41 +01:00
|
|
|
// TODO
|
|
|
|
|
byte sfLength = data[systemAreaOff + 2];
|
|
|
|
|
systemAreaOff += sfLength;
|
|
|
|
|
|
|
|
|
|
break;
|
2019-07-28 17:04:08 +01:00
|
|
|
case SUSP_CONTINUATION:
|
2019-07-28 18:41:33 +01:00
|
|
|
byte ceLength = data[systemAreaOff + 2];
|
|
|
|
|
|
|
|
|
|
ContinuationArea ca =
|
|
|
|
|
Marshal.ByteArrayToStructureLittleEndian<ContinuationArea>(data, systemAreaOff,
|
2020-11-07 01:17:06 +00:00
|
|
|
Marshal.SizeOf<ContinuationArea>());
|
2019-07-28 18:41:33 +01:00
|
|
|
|
2020-03-10 19:31:34 +00:00
|
|
|
byte[] caData = ReadSingleExtent(ca.offset, ca.ca_length, ca.block);
|
2019-07-28 18:41:33 +01:00
|
|
|
|
2020-03-10 19:31:34 +00:00
|
|
|
DecodeSystemArea(caData, 0, (int)ca.ca_length, ref entry, out hasResourceFork);
|
2019-07-28 18:41:33 +01:00
|
|
|
|
|
|
|
|
systemAreaOff += ceLength;
|
2020-02-19 01:20:23 +00:00
|
|
|
|
2019-07-28 18:41:33 +01:00
|
|
|
break;
|
2019-07-28 17:04:08 +01:00
|
|
|
case SUSP_PADDING:
|
2019-07-28 19:10:42 +01:00
|
|
|
// Just padding, skip
|
|
|
|
|
byte pdLength = data[systemAreaOff + 2];
|
|
|
|
|
systemAreaOff += pdLength;
|
|
|
|
|
|
|
|
|
|
break;
|
2019-07-28 17:04:08 +01:00
|
|
|
case SUSP_INDICATOR:
|
2019-07-28 19:10:42 +01:00
|
|
|
// Only to be found on CURRENT entry of root directory
|
|
|
|
|
byte spLength = data[systemAreaOff + 2];
|
|
|
|
|
systemAreaOff += spLength;
|
|
|
|
|
|
|
|
|
|
break;
|
2019-07-28 17:04:08 +01:00
|
|
|
case SUSP_TERMINATOR:
|
2019-07-28 19:10:42 +01:00
|
|
|
// Not seen on the wild
|
|
|
|
|
byte stLength = data[systemAreaOff + 2];
|
|
|
|
|
systemAreaOff += stLength;
|
|
|
|
|
|
|
|
|
|
break;
|
2019-07-28 17:04:08 +01:00
|
|
|
case SUSP_REFERENCE:
|
2019-07-28 19:10:42 +01:00
|
|
|
// Only to be found on CURRENT entry of root directory
|
|
|
|
|
byte erLength = data[systemAreaOff + 2];
|
|
|
|
|
systemAreaOff += erLength;
|
|
|
|
|
|
|
|
|
|
break;
|
2019-07-28 17:04:08 +01:00
|
|
|
case SUSP_SELECTOR:
|
2019-07-28 19:10:42 +01:00
|
|
|
// Only to be found on CURRENT entry of root directory
|
|
|
|
|
byte esLength = data[systemAreaOff + 2];
|
|
|
|
|
systemAreaOff += esLength;
|
|
|
|
|
|
|
|
|
|
break;
|
2019-07-28 17:04:08 +01:00
|
|
|
case ZISO_MAGIC:
|
2019-07-28 19:10:42 +01:00
|
|
|
// TODO: Implement support for zisofs
|
|
|
|
|
byte zfLength = data[systemAreaOff + 2];
|
|
|
|
|
systemAreaOff += zfLength;
|
2019-07-28 17:04:08 +01:00
|
|
|
|
2019-07-28 16:55:15 +01:00
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
// Cannot continue as we don't know this structure size
|
|
|
|
|
systemAreaOff = end;
|
2020-02-19 01:20:23 +00:00
|
|
|
|
2019-07-28 16:55:15 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-07-29 04:00:51 +01:00
|
|
|
|
|
|
|
|
PathTableEntryInternal[] GetPathTableEntries(string path)
|
|
|
|
|
{
|
2019-07-31 19:53:47 +01:00
|
|
|
IEnumerable<PathTableEntryInternal> tableEntries;
|
2020-07-20 21:11:32 +01:00
|
|
|
List<PathTableEntryInternal> pathTableList = new List<PathTableEntryInternal>(_pathTable);
|
2019-07-29 04:00:51 +01:00
|
|
|
|
2020-02-19 01:20:23 +00:00
|
|
|
if(path == "" ||
|
|
|
|
|
path == "/")
|
2020-07-20 21:11:32 +01:00
|
|
|
tableEntries = _pathTable.Where(p => p.Parent == 1 && p != _pathTable[0]);
|
2019-07-29 04:00:51 +01:00
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
string cutPath = path.StartsWith("/", StringComparison.Ordinal)
|
|
|
|
|
? path.Substring(1).ToLower(CultureInfo.CurrentUICulture)
|
|
|
|
|
: path.ToLower(CultureInfo.CurrentUICulture);
|
|
|
|
|
|
2020-02-19 01:20:23 +00:00
|
|
|
string[] pieces = cutPath.Split(new[]
|
|
|
|
|
{
|
|
|
|
|
'/'
|
|
|
|
|
}, StringSplitOptions.RemoveEmptyEntries);
|
2019-07-29 04:00:51 +01:00
|
|
|
|
|
|
|
|
int currentParent = 1;
|
|
|
|
|
int currentPiece = 0;
|
|
|
|
|
|
|
|
|
|
while(currentPiece < pieces.Length)
|
|
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
PathTableEntryInternal currentEntry = _pathTable.FirstOrDefault(p => p.Parent == currentParent &&
|
2020-11-07 01:17:06 +00:00
|
|
|
p.Name.ToLower(CultureInfo.CurrentUICulture) == pieces[currentPiece]);
|
2019-07-29 04:00:51 +01:00
|
|
|
|
2020-02-19 01:20:23 +00:00
|
|
|
if(currentEntry is null)
|
|
|
|
|
break;
|
2019-07-29 04:00:51 +01:00
|
|
|
|
|
|
|
|
currentPiece++;
|
|
|
|
|
currentParent = pathTableList.IndexOf(currentEntry) + 1;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
tableEntries = _pathTable.Where(p => p.Parent == currentParent);
|
2019-07-29 04:00:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return tableEntries.ToArray();
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-31 05:19:18 +01:00
|
|
|
DecodedDirectoryEntry[] GetSubdirsFromCdiPathTable(string path)
|
|
|
|
|
{
|
|
|
|
|
PathTableEntryInternal[] tableEntries = GetPathTableEntries(path);
|
|
|
|
|
List<DecodedDirectoryEntry> entries = new List<DecodedDirectoryEntry>();
|
2020-02-19 01:20:23 +00:00
|
|
|
|
2019-07-31 05:19:18 +01:00
|
|
|
foreach(PathTableEntryInternal tEntry in tableEntries)
|
|
|
|
|
{
|
2020-03-10 19:31:34 +00:00
|
|
|
byte[] sector = ReadSector(tEntry.Extent);
|
2020-02-19 01:20:23 +00:00
|
|
|
|
2019-07-31 05:19:18 +01:00
|
|
|
CdiDirectoryRecord record =
|
2019-07-31 05:47:28 +01:00
|
|
|
Marshal.ByteArrayToStructureBigEndian<CdiDirectoryRecord>(sector, tEntry.XattrLength,
|
2020-07-20 21:11:32 +01:00
|
|
|
_cdiDirectoryRecordSize);
|
2019-07-31 05:19:18 +01:00
|
|
|
|
2020-02-19 01:20:23 +00:00
|
|
|
if(record.length == 0)
|
|
|
|
|
break;
|
2019-07-31 05:19:18 +01:00
|
|
|
|
2020-02-19 01:20:23 +00:00
|
|
|
var entry = new DecodedDirectoryEntry
|
2019-07-31 05:19:18 +01:00
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
Size = record.size,
|
|
|
|
|
Filename = tEntry.Name,
|
2019-07-31 05:19:18 +01:00
|
|
|
VolumeSequenceNumber = record.volume_sequence_number,
|
2020-07-20 04:34:16 +01:00
|
|
|
Timestamp = DecodeHighSierraDateTime(record.date),
|
|
|
|
|
XattrLength = tEntry.XattrLength
|
2019-07-31 05:19:18 +01:00
|
|
|
};
|
|
|
|
|
|
2019-07-31 19:39:55 +01:00
|
|
|
if(record.size != 0)
|
2020-02-19 01:20:23 +00:00
|
|
|
entry.Extents = new List<(uint extent, uint size)>
|
|
|
|
|
{
|
|
|
|
|
(record.start_lbn, record.size)
|
|
|
|
|
};
|
2019-07-31 19:39:55 +01:00
|
|
|
|
2020-02-19 01:20:23 +00:00
|
|
|
if(record.flags.HasFlag(CdiFileFlags.Hidden))
|
|
|
|
|
entry.Flags |= FileFlags.Hidden;
|
2019-07-31 05:19:18 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
int systemAreaStart = record.name_len + _cdiDirectoryRecordSize;
|
2019-07-31 23:02:43 +01:00
|
|
|
|
2020-02-19 01:20:23 +00:00
|
|
|
if(systemAreaStart % 2 != 0)
|
|
|
|
|
systemAreaStart++;
|
2019-07-31 23:02:43 +01:00
|
|
|
|
2019-07-31 05:19:18 +01:00
|
|
|
entry.CdiSystemArea =
|
2020-07-20 21:11:32 +01:00
|
|
|
Marshal.ByteArrayToStructureBigEndian<CdiSystemArea>(sector, systemAreaStart, _cdiSystemAreaSize);
|
2019-07-31 05:19:18 +01:00
|
|
|
|
2020-02-19 01:20:23 +00:00
|
|
|
if(entry.CdiSystemArea.Value.attributes.HasFlag(CdiAttributes.Directory))
|
2019-07-31 05:19:18 +01:00
|
|
|
entry.Flags |= FileFlags.Directory;
|
|
|
|
|
|
|
|
|
|
entries.Add(entry);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return entries.ToArray();
|
|
|
|
|
}
|
2019-07-29 04:00:51 +01:00
|
|
|
|
|
|
|
|
DecodedDirectoryEntry[] GetSubdirsFromIsoPathTable(string path)
|
|
|
|
|
{
|
|
|
|
|
PathTableEntryInternal[] tableEntries = GetPathTableEntries(path);
|
|
|
|
|
List<DecodedDirectoryEntry> entries = new List<DecodedDirectoryEntry>();
|
2020-02-19 01:20:23 +00:00
|
|
|
|
2019-07-29 04:00:51 +01:00
|
|
|
foreach(PathTableEntryInternal tEntry in tableEntries)
|
|
|
|
|
{
|
2020-03-10 19:31:34 +00:00
|
|
|
byte[] sector = ReadSector(tEntry.Extent);
|
2020-02-19 01:20:23 +00:00
|
|
|
|
2019-07-29 04:00:51 +01:00
|
|
|
DirectoryRecord record =
|
2019-07-31 05:47:28 +01:00
|
|
|
Marshal.ByteArrayToStructureLittleEndian<DirectoryRecord>(sector, tEntry.XattrLength,
|
2020-07-20 21:11:32 +01:00
|
|
|
_directoryRecordSize);
|
2019-07-29 04:00:51 +01:00
|
|
|
|
2020-02-19 01:20:23 +00:00
|
|
|
if(record.length == 0)
|
|
|
|
|
break;
|
2019-07-29 04:00:51 +01:00
|
|
|
|
2020-02-19 01:20:23 +00:00
|
|
|
var entry = new DecodedDirectoryEntry
|
2019-07-29 04:00:51 +01:00
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
Size = record.size,
|
|
|
|
|
Flags = record.flags,
|
2019-07-29 04:00:51 +01:00
|
|
|
Filename = tEntry.Name,
|
2020-07-20 04:34:16 +01:00
|
|
|
FileUnitSize = record.file_unit_size,
|
|
|
|
|
Interleave = record.interleave,
|
|
|
|
|
VolumeSequenceNumber = record.volume_sequence_number,
|
|
|
|
|
Timestamp = DecodeIsoDateTime(record.date),
|
2019-07-31 05:47:28 +01:00
|
|
|
XattrLength = tEntry.XattrLength
|
2019-07-29 04:00:51 +01:00
|
|
|
};
|
|
|
|
|
|
2020-02-19 01:20:23 +00:00
|
|
|
if(record.size != 0)
|
|
|
|
|
entry.Extents = new List<(uint extent, uint size)>
|
|
|
|
|
{
|
|
|
|
|
(record.extent, record.size)
|
|
|
|
|
};
|
2019-07-31 19:39:55 +01:00
|
|
|
|
2020-11-07 01:17:06 +00:00
|
|
|
int systemAreaStart = record.name_len + _directoryRecordSize;
|
|
|
|
|
int systemAreaLength = record.length - record.name_len - _directoryRecordSize;
|
2019-07-29 04:00:51 +01:00
|
|
|
|
|
|
|
|
if(systemAreaStart % 2 != 0)
|
|
|
|
|
{
|
|
|
|
|
systemAreaStart++;
|
|
|
|
|
systemAreaLength--;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DecodeSystemArea(sector, systemAreaStart, systemAreaStart + systemAreaLength, ref entry, out _);
|
|
|
|
|
|
|
|
|
|
entries.Add(entry);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return entries.ToArray();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DecodedDirectoryEntry[] GetSubdirsFromHighSierraPathTable(string path)
|
|
|
|
|
{
|
|
|
|
|
PathTableEntryInternal[] tableEntries = GetPathTableEntries(path);
|
|
|
|
|
List<DecodedDirectoryEntry> entries = new List<DecodedDirectoryEntry>();
|
2020-02-19 01:20:23 +00:00
|
|
|
|
2019-07-29 04:00:51 +01:00
|
|
|
foreach(PathTableEntryInternal tEntry in tableEntries)
|
|
|
|
|
{
|
2020-03-10 19:31:34 +00:00
|
|
|
byte[] sector = ReadSector(tEntry.Extent);
|
2020-02-19 01:20:23 +00:00
|
|
|
|
2019-07-29 04:00:51 +01:00
|
|
|
HighSierraDirectoryRecord record =
|
2019-07-31 05:47:28 +01:00
|
|
|
Marshal.ByteArrayToStructureLittleEndian<HighSierraDirectoryRecord>(sector, tEntry.XattrLength,
|
2020-11-07 01:17:06 +00:00
|
|
|
_highSierraDirectoryRecordSize);
|
2019-07-29 04:00:51 +01:00
|
|
|
|
2020-02-19 01:20:23 +00:00
|
|
|
var entry = new DecodedDirectoryEntry
|
2019-07-29 04:00:51 +01:00
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
Size = record.size,
|
|
|
|
|
Flags = record.flags,
|
|
|
|
|
Filename = tEntry.Name,
|
2019-07-29 04:00:51 +01:00
|
|
|
Interleave = record.interleave,
|
|
|
|
|
VolumeSequenceNumber = record.volume_sequence_number,
|
2020-07-20 04:34:16 +01:00
|
|
|
Timestamp = DecodeHighSierraDateTime(record.date),
|
|
|
|
|
XattrLength = tEntry.XattrLength
|
2019-07-29 04:00:51 +01:00
|
|
|
};
|
|
|
|
|
|
2020-02-19 01:20:23 +00:00
|
|
|
if(record.size != 0)
|
|
|
|
|
entry.Extents = new List<(uint extent, uint size)>
|
|
|
|
|
{
|
|
|
|
|
(record.extent, record.size)
|
|
|
|
|
};
|
2019-07-31 19:39:55 +01:00
|
|
|
|
2019-07-29 04:00:51 +01:00
|
|
|
entries.Add(entry);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return entries.ToArray();
|
|
|
|
|
}
|
2019-07-19 12:14:30 +01:00
|
|
|
}
|
|
|
|
|
}
|