public Type1Partition(UdfContext context, LogicalVolumeDescriptor volumeDescriptor,
                       Type1PartitionMap partitionMap)
     : base(context, volumeDescriptor)
 {
     _partitionMap = partitionMap;
     _physical     = context.PhysicalPartitions[partitionMap.PartitionNumber];
 }
Beispiel #2
0
        public MetadataPartition(UdfContext context, LogicalVolumeDescriptor volumeDescriptor, MetadataPartitionMap partitionMap)
            : base(context, volumeDescriptor)
        {
            _partitionMap = partitionMap;

            PhysicalPartition physical = context.PhysicalPartitions[partitionMap.PartitionNumber];
            long fileEntryPos = partitionMap.MetadataFileLocation * (long)volumeDescriptor.LogicalBlockSize;

            byte[] entryData = Utilities.ReadFully(physical.Content, fileEntryPos, _context.PhysicalSectorSize);
            if (!DescriptorTag.IsValid(entryData, 0))
            {
                throw new IOException("Invalid descriptor tag looking for Metadata file entry");
            }

            DescriptorTag dt = Utilities.ToStruct<DescriptorTag>(entryData, 0);
            if (dt.TagIdentifier == TagIdentifier.ExtendedFileEntry)
            {
                ExtendedFileEntry efe = Utilities.ToStruct<ExtendedFileEntry>(entryData, 0);
                _metadataFile = new File(context, physical, efe, _volumeDescriptor.LogicalBlockSize);
            }
            else
            {
                throw new NotImplementedException("Only EFE implemented for Metadata file entry");
            }
        }
Beispiel #3
0
        public static LogicalPartition FromDescriptor(UdfContext context, LogicalVolumeDescriptor volumeDescriptor, int index)
        {
            PartitionMap map = volumeDescriptor.PartitionMaps[index];

            Type1PartitionMap asType1 = map as Type1PartitionMap;
            if (asType1 != null)
            {
                return new Type1Partition(context, volumeDescriptor, asType1);
            }

            MetadataPartitionMap asMetadata = map as MetadataPartitionMap;
            if (asMetadata != null)
            {
                return new MetadataPartition(context, volumeDescriptor, asMetadata);
            }

            throw new NotImplementedException("Unrecognized partition map type");
        }
Beispiel #4
0
        public bool Identify(IMediaImage imagePlugin, Partition partition)
        {
            // UDF needs at least that
            if (partition.End - partition.Start < 256)
            {
                return(false);
            }

            // UDF needs at least that
            if (imagePlugin.Info.SectorSize < 512)
            {
                return(false);
            }

            byte[] sector;
            AnchorVolumeDescriptorPointer anchor = new AnchorVolumeDescriptorPointer();

            // All positions where anchor may reside
            ulong[] positions   = { 256, 512, partition.End - 256, partition.End };
            bool    anchorFound = false;

            foreach (ulong position in positions.Where(position => position + partition.Start < partition.End))
            {
                sector = imagePlugin.ReadSector(position);
                anchor = new AnchorVolumeDescriptorPointer();
                IntPtr anchorPtr = Marshal.AllocHGlobal(Marshal.SizeOf(anchor));
                Marshal.Copy(sector, 0, anchorPtr, Marshal.SizeOf(anchor));
                anchor =
                    (AnchorVolumeDescriptorPointer)Marshal.PtrToStructure(anchorPtr,
                                                                          typeof(AnchorVolumeDescriptorPointer));
                Marshal.FreeHGlobal(anchorPtr);

                DicConsole.DebugWriteLine("UDF Plugin", "anchor.tag.tagIdentifier = {0}", anchor.tag.tagIdentifier);
                DicConsole.DebugWriteLine("UDF Plugin", "anchor.tag.descriptorVersion = {0}",
                                          anchor.tag.descriptorVersion);
                DicConsole.DebugWriteLine("UDF Plugin", "anchor.tag.tagChecksum = 0x{0:X2}", anchor.tag.tagChecksum);
                DicConsole.DebugWriteLine("UDF Plugin", "anchor.tag.reserved = {0}", anchor.tag.reserved);
                DicConsole.DebugWriteLine("UDF Plugin", "anchor.tag.tagSerialNumber = {0}", anchor.tag.tagSerialNumber);
                DicConsole.DebugWriteLine("UDF Plugin", "anchor.tag.descriptorCrc = 0x{0:X4}",
                                          anchor.tag.descriptorCrc);
                DicConsole.DebugWriteLine("UDF Plugin", "anchor.tag.descriptorCrcLength = {0}",
                                          anchor.tag.descriptorCrcLength);
                DicConsole.DebugWriteLine("UDF Plugin", "anchor.tag.tagLocation = {0}", anchor.tag.tagLocation);
                DicConsole.DebugWriteLine("UDF Plugin", "anchor.mainVolumeDescriptorSequenceExtent.length = {0}",
                                          anchor.mainVolumeDescriptorSequenceExtent.length);
                DicConsole.DebugWriteLine("UDF Plugin", "anchor.mainVolumeDescriptorSequenceExtent.location = {0}",
                                          anchor.mainVolumeDescriptorSequenceExtent.location);
                DicConsole.DebugWriteLine("UDF Plugin", "anchor.reserveVolumeDescriptorSequenceExtent.length = {0}",
                                          anchor.reserveVolumeDescriptorSequenceExtent.length);
                DicConsole.DebugWriteLine("UDF Plugin", "anchor.reserveVolumeDescriptorSequenceExtent.location = {0}",
                                          anchor.reserveVolumeDescriptorSequenceExtent.location);

                if (anchor.tag.tagIdentifier != TagIdentifier.AnchorVolumeDescriptorPointer ||
                    anchor.tag.tagLocation != position ||
                    anchor.mainVolumeDescriptorSequenceExtent.location + partition.Start >= partition.End)
                {
                    continue;
                }

                anchorFound = true;
                break;
            }

            if (!anchorFound)
            {
                return(false);
            }

            ulong count = 0;

            while (count < 256)
            {
                sector = imagePlugin.ReadSector(partition.Start + anchor.mainVolumeDescriptorSequenceExtent.location +
                                                count);
                TagIdentifier tagId    = (TagIdentifier)BitConverter.ToUInt16(sector, 0);
                uint          location = BitConverter.ToUInt32(sector, 0x0C);

                if (location == partition.Start + anchor.mainVolumeDescriptorSequenceExtent.location + count)
                {
                    if (tagId == TagIdentifier.TerminatingDescriptor)
                    {
                        break;
                    }

                    if (tagId == TagIdentifier.LogicalVolumeDescriptor)
                    {
                        LogicalVolumeDescriptor lvd = new LogicalVolumeDescriptor();
                        IntPtr lvdPtr = Marshal.AllocHGlobal(Marshal.SizeOf(lvd));
                        Marshal.Copy(sector, 0, lvdPtr, Marshal.SizeOf(lvd));
                        lvd = (LogicalVolumeDescriptor)Marshal.PtrToStructure(lvdPtr, typeof(LogicalVolumeDescriptor));
                        Marshal.FreeHGlobal(lvdPtr);

                        return(UDF_Magic.SequenceEqual(lvd.domainIdentifier.identifier));
                    }
                }
                else
                {
                    break;
                }

                count++;
            }

            return(false);
        }
Beispiel #5
0
        public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information,
                                   Encoding encoding)
        {
            // UDF is always UTF-8
            Encoding = Encoding.UTF8;
            byte[] sector;

            StringBuilder sbInformation = new StringBuilder();

            sbInformation.AppendLine("Universal Disk Format");

            AnchorVolumeDescriptorPointer anchor = new AnchorVolumeDescriptorPointer();

            // All positions where anchor may reside
            ulong[] positions = { 256, 512, partition.End - 256, partition.End };

            foreach (ulong position in positions)
            {
                sector = imagePlugin.ReadSector(position);
                anchor = new AnchorVolumeDescriptorPointer();
                IntPtr anchorPtr = Marshal.AllocHGlobal(Marshal.SizeOf(anchor));
                Marshal.Copy(sector, 0, anchorPtr, Marshal.SizeOf(anchor));
                anchor =
                    (AnchorVolumeDescriptorPointer)Marshal.PtrToStructure(anchorPtr,
                                                                          typeof(AnchorVolumeDescriptorPointer));
                Marshal.FreeHGlobal(anchorPtr);

                if (anchor.tag.tagIdentifier == TagIdentifier.AnchorVolumeDescriptorPointer &&
                    anchor.tag.tagLocation == position &&
                    anchor.mainVolumeDescriptorSequenceExtent.location + partition.Start < partition.End)
                {
                    break;
                }
            }

            ulong count = 0;

            PrimaryVolumeDescriptor          pvd  = new PrimaryVolumeDescriptor();
            LogicalVolumeDescriptor          lvd  = new LogicalVolumeDescriptor();
            LogicalVolumeIntegrityDescriptor lvid = new LogicalVolumeIntegrityDescriptor();
            LogicalVolumeIntegrityDescriptorImplementationUse lvidiu =
                new LogicalVolumeIntegrityDescriptorImplementationUse();

            while (count < 256)
            {
                sector = imagePlugin.ReadSector(partition.Start + anchor.mainVolumeDescriptorSequenceExtent.location +
                                                count);
                TagIdentifier tagId    = (TagIdentifier)BitConverter.ToUInt16(sector, 0);
                uint          location = BitConverter.ToUInt32(sector, 0x0C);

                if (location == partition.Start + anchor.mainVolumeDescriptorSequenceExtent.location + count)
                {
                    if (tagId == TagIdentifier.TerminatingDescriptor)
                    {
                        break;
                    }

                    switch (tagId)
                    {
                    case TagIdentifier.LogicalVolumeDescriptor:
                        IntPtr lvdPtr = Marshal.AllocHGlobal(Marshal.SizeOf(lvd));
                        Marshal.Copy(sector, 0, lvdPtr, Marshal.SizeOf(lvd));
                        lvd = (LogicalVolumeDescriptor)
                              Marshal.PtrToStructure(lvdPtr, typeof(LogicalVolumeDescriptor));
                        Marshal.FreeHGlobal(lvdPtr);
                        break;

                    case TagIdentifier.PrimaryVolumeDescriptor:
                        IntPtr pvdPtr = Marshal.AllocHGlobal(Marshal.SizeOf(pvd));
                        Marshal.Copy(sector, 0, pvdPtr, Marshal.SizeOf(pvd));
                        pvd = (PrimaryVolumeDescriptor)
                              Marshal.PtrToStructure(pvdPtr, typeof(PrimaryVolumeDescriptor));
                        Marshal.FreeHGlobal(pvdPtr);
                        break;
                    }
                }
                else
                {
                    break;
                }

                count++;
            }

            sector = imagePlugin.ReadSector(lvd.integritySequenceExtent.location);
            IntPtr lvidPtr = Marshal.AllocHGlobal(Marshal.SizeOf(lvid));

            Marshal.Copy(sector, 0, lvidPtr, Marshal.SizeOf(lvid));
            lvid =
                (LogicalVolumeIntegrityDescriptor)
                Marshal.PtrToStructure(lvidPtr, typeof(LogicalVolumeIntegrityDescriptor));
            Marshal.FreeHGlobal(lvidPtr);

            if (lvid.tag.tagIdentifier == TagIdentifier.LogicalVolumeIntegrityDescriptor &&
                lvid.tag.tagLocation == lvd.integritySequenceExtent.location)
            {
                IntPtr lvidiuPtr = Marshal.AllocHGlobal(Marshal.SizeOf(lvidiu));
                Marshal.Copy(sector, (int)(lvid.numberOfPartitions * 8 + 80), lvidiuPtr, Marshal.SizeOf(lvidiu));
                lvidiu =
                    (LogicalVolumeIntegrityDescriptorImplementationUse)Marshal.PtrToStructure(lvidiuPtr,
                                                                                              typeof(
                                                                                                  LogicalVolumeIntegrityDescriptorImplementationUse
                                                                                                  ));
                Marshal.FreeHGlobal(lvidiuPtr);
            }
            else
            {
                lvid = new LogicalVolumeIntegrityDescriptor();
            }

            sbInformation.AppendFormat("Volume is number {0} of {1}", pvd.volumeSequenceNumber,
                                       pvd.maximumVolumeSequenceNumber).AppendLine();
            sbInformation.AppendFormat("Volume set identifier: {0}",
                                       StringHandlers.DecompressUnicode(pvd.volumeSetIdentifier)).AppendLine();
            sbInformation
            .AppendFormat("Volume name: {0}", StringHandlers.DecompressUnicode(lvd.logicalVolumeIdentifier))
            .AppendLine();
            sbInformation.AppendFormat("Volume uses {0} bytes per block", lvd.logicalBlockSize).AppendLine();
            sbInformation.AppendFormat("Volume was las written in {0}", EcmaToDateTime(lvid.recordingDateTime))
            .AppendLine();
            sbInformation.AppendFormat("Volume contains {0} partitions", lvid.numberOfPartitions).AppendLine();
            sbInformation
            .AppendFormat("Volume contains {0} files and {1} directories", lvidiu.files, lvidiu.directories)
            .AppendLine();
            sbInformation.AppendFormat("Volume conforms to {0}",
                                       Encoding.GetString(lvd.domainIdentifier.identifier).TrimEnd('\u0000'))
            .AppendLine();
            sbInformation.AppendFormat("Volume was last written by: {0}",
                                       Encoding.GetString(pvd.implementationIdentifier.identifier).TrimEnd('\u0000'))
            .AppendLine();
            sbInformation.AppendFormat("Volume requires UDF version {0}.{1:X2} to be read",
                                       Convert.ToInt32($"{(lvidiu.minimumReadUDF & 0xFF00) >> 8}", 10),
                                       Convert.ToInt32($"{lvidiu.minimumReadUDF & 0xFF}", 10)).AppendLine();
            sbInformation.AppendFormat("Volume requires UDF version {0}.{1:X2} to be written to",
                                       Convert.ToInt32($"{(lvidiu.minimumWriteUDF & 0xFF00) >> 8}", 10),
                                       Convert.ToInt32($"{lvidiu.minimumWriteUDF & 0xFF}", 10)).AppendLine();
            sbInformation.AppendFormat("Volume cannot be written by any UDF version higher than {0}.{1:X2}",
                                       Convert.ToInt32($"{(lvidiu.maximumWriteUDF & 0xFF00) >> 8}", 10),
                                       Convert.ToInt32($"{lvidiu.maximumWriteUDF & 0xFF}", 10)).AppendLine();

            XmlFsType = new FileSystemType
            {
                Type =
                    $"UDF v{Convert.ToInt32($"{(lvidiu.maximumWriteUDF & 0xFF00) >> 8}", 10)}.{Convert.ToInt32($"{lvidiu.maximumWriteUDF & 0xFF}", 10):X2}",
                ApplicationIdentifier     = Encoding.GetString(pvd.implementationIdentifier.identifier).TrimEnd('\u0000'),
                ClusterSize               = (int)lvd.logicalBlockSize,
                ModificationDate          = EcmaToDateTime(lvid.recordingDateTime),
                ModificationDateSpecified = true,
                Files               = lvidiu.files,
                FilesSpecified      = true,
                VolumeName          = StringHandlers.DecompressUnicode(lvd.logicalVolumeIdentifier),
                VolumeSetIdentifier = StringHandlers.DecompressUnicode(pvd.volumeSetIdentifier),
                SystemIdentifier    = Encoding.GetString(pvd.implementationIdentifier.identifier).TrimEnd('\u0000')
            };
            XmlFsType.Clusters = (long)((partition.End - partition.Start + 1) * imagePlugin.Info.SectorSize /
                                        (ulong)XmlFsType.ClusterSize);

            information = sbInformation.ToString();
        }
Beispiel #6
0
 protected LogicalPartition(UdfContext context, LogicalVolumeDescriptor volumeDescriptor)
 {
     _context = context;
     _volumeDescriptor = volumeDescriptor;
 }
Beispiel #7
0
 public Type1Partition(UdfContext context, LogicalVolumeDescriptor volumeDescriptor, Type1PartitionMap partitionMap)
     : base(context, volumeDescriptor)
 {
     _partitionMap = partitionMap;
     _physical = context.PhysicalPartitions[partitionMap.PartitionNumber];
 }
Beispiel #8
0
        public bool Identify(IMediaImage imagePlugin, Partition partition)
        {
            // UDF needs at least that
            if(partition.End - partition.Start < 256)
                return false;

            // UDF needs at least that
            if(imagePlugin.Info.SectorSize < 512)
                return false;

            byte[] sector;
            var    anchor = new AnchorVolumeDescriptorPointer();

            // All positions where anchor may reside, with the ratio between 512 and 2048bps
            ulong[][] positions =
            {
                new ulong[]
                {
                    256, 1
                },
                new ulong[]
                {
                    512, 1
                },
                new ulong[]
                {
                    partition.End - 256, 1
                },
                new ulong[]
                {
                    partition.End, 1
                },
                new ulong[]
                {
                    1024, 4
                },
                new ulong[]
                {
                    2048, 4
                },
                new ulong[]
                {
                    partition.End - 1024, 4
                },
                new ulong[]
                {
                    partition.End - 4, 4
                }
            };

            bool anchorFound = false;
            uint ratio       = 1;

            foreach(ulong[] position in positions.Where(position => position[0] + partition.Start + position[1] <=
                                                                    partition.End && position[0] < partition.End))
            {
                sector = imagePlugin.ReadSectors(position[0], (uint)position[1]);
                anchor = Marshal.ByteArrayToStructureLittleEndian<AnchorVolumeDescriptorPointer>(sector);

                AaruConsole.DebugWriteLine("UDF Plugin", "anchor.tag.tagIdentifier = {0}", anchor.tag.tagIdentifier);

                AaruConsole.DebugWriteLine("UDF Plugin", "anchor.tag.descriptorVersion = {0}",
                                           anchor.tag.descriptorVersion);

                AaruConsole.DebugWriteLine("UDF Plugin", "anchor.tag.tagChecksum = 0x{0:X2}", anchor.tag.tagChecksum);
                AaruConsole.DebugWriteLine("UDF Plugin", "anchor.tag.reserved = {0}", anchor.tag.reserved);

                AaruConsole.DebugWriteLine("UDF Plugin", "anchor.tag.tagSerialNumber = {0}",
                                           anchor.tag.tagSerialNumber);

                AaruConsole.DebugWriteLine("UDF Plugin", "anchor.tag.descriptorCrc = 0x{0:X4}",
                                           anchor.tag.descriptorCrc);

                AaruConsole.DebugWriteLine("UDF Plugin", "anchor.tag.descriptorCrcLength = {0}",
                                           anchor.tag.descriptorCrcLength);

                AaruConsole.DebugWriteLine("UDF Plugin", "anchor.tag.tagLocation = {0}", anchor.tag.tagLocation);

                AaruConsole.DebugWriteLine("UDF Plugin", "anchor.mainVolumeDescriptorSequenceExtent.length = {0}",
                                           anchor.mainVolumeDescriptorSequenceExtent.length);

                AaruConsole.DebugWriteLine("UDF Plugin", "anchor.mainVolumeDescriptorSequenceExtent.location = {0}",
                                           anchor.mainVolumeDescriptorSequenceExtent.location);

                AaruConsole.DebugWriteLine("UDF Plugin", "anchor.reserveVolumeDescriptorSequenceExtent.length = {0}",
                                           anchor.reserveVolumeDescriptorSequenceExtent.length);

                AaruConsole.DebugWriteLine("UDF Plugin", "anchor.reserveVolumeDescriptorSequenceExtent.location = {0}",
                                           anchor.reserveVolumeDescriptorSequenceExtent.location);

                if(anchor.tag.tagIdentifier != TagIdentifier.AnchorVolumeDescriptorPointer ||
                   anchor.tag.tagLocation   != position[0] / position[1]                   ||
                   (anchor.mainVolumeDescriptorSequenceExtent.location * position[1]) + partition.Start >=
                   partition.End)
                    continue;

                anchorFound = true;
                ratio       = (uint)position[1];

                break;
            }

            if(!anchorFound)
                return false;

            ulong count = 0;

            while(count < 256)
            {
                sector =
                    imagePlugin.
                        ReadSectors(partition.Start + (anchor.mainVolumeDescriptorSequenceExtent.location * ratio) + (count * ratio),
                                    ratio);

                var  tagId    = (TagIdentifier)BitConverter.ToUInt16(sector, 0);
                uint location = BitConverter.ToUInt32(sector, 0x0C);

                if(location == (partition.Start / ratio) + anchor.mainVolumeDescriptorSequenceExtent.location + count)
                {
                    if(tagId == TagIdentifier.TerminatingDescriptor)
                        break;

                    if(tagId == TagIdentifier.LogicalVolumeDescriptor)
                    {
                        LogicalVolumeDescriptor lvd =
                            Marshal.ByteArrayToStructureLittleEndian<LogicalVolumeDescriptor>(sector);

                        return _magic.SequenceEqual(lvd.domainIdentifier.identifier);
                    }
                }
                else
                    break;

                count++;
            }

            return false;
        }
Beispiel #9
0
        public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information,
                                   Encoding encoding)
        {
            // UDF is always UTF-8
            Encoding = Encoding.UTF8;
            byte[] sector;

            var sbInformation = new StringBuilder();

            sbInformation.AppendLine("Universal Disk Format");

            var anchor = new AnchorVolumeDescriptorPointer();

            // All positions where anchor may reside, with the ratio between 512 and 2048bps
            ulong[][] positions =
            {
                new ulong[]
                {
                    256, 1
                },
                new ulong[]
                {
                    512, 1
                },
                new ulong[]
                {
                    partition.End - 256, 1
                },
                new ulong[]
                {
                    partition.End, 1
                },
                new ulong[]
                {
                    1024, 4
                },
                new ulong[]
                {
                    2048, 4
                },
                new ulong[]
                {
                    partition.End - 1024, 4
                },
                new ulong[]
                {
                    partition.End - 4, 4
                }
            };

            uint ratio = 1;

            foreach(ulong[] position in positions)
            {
                sector = imagePlugin.ReadSectors(position[0], (uint)position[1]);
                anchor = Marshal.ByteArrayToStructureLittleEndian<AnchorVolumeDescriptorPointer>(sector);

                if(anchor.tag.tagIdentifier != TagIdentifier.AnchorVolumeDescriptorPointer ||
                   anchor.tag.tagLocation != position[0] / position[1] ||
                   anchor.mainVolumeDescriptorSequenceExtent.location + partition.Start >= partition.End)
                    continue;

                ratio = (uint)position[1];

                break;
            }

            ulong count = 0;

            var pvd    = new PrimaryVolumeDescriptor();
            var lvd    = new LogicalVolumeDescriptor();
            var lvid   = new LogicalVolumeIntegrityDescriptor();
            var lvidiu = new LogicalVolumeIntegrityDescriptorImplementationUse();

            while(count < 256)
            {
                sector =
                    imagePlugin.
                        ReadSectors(partition.Start + (anchor.mainVolumeDescriptorSequenceExtent.location * ratio) + (count * ratio),
                                    ratio);

                var  tagId    = (TagIdentifier)BitConverter.ToUInt16(sector, 0);
                uint location = BitConverter.ToUInt32(sector, 0x0C);

                if(location == (partition.Start / ratio) + anchor.mainVolumeDescriptorSequenceExtent.location + count)
                {
                    if(tagId == TagIdentifier.TerminatingDescriptor)
                        break;

                    switch(tagId)
                    {
                        case TagIdentifier.LogicalVolumeDescriptor:
                            lvd = Marshal.ByteArrayToStructureLittleEndian<LogicalVolumeDescriptor>(sector);

                            break;
                        case TagIdentifier.PrimaryVolumeDescriptor:
                            pvd = Marshal.ByteArrayToStructureLittleEndian<PrimaryVolumeDescriptor>(sector);

                            break;
                    }
                }
                else
                    break;

                count++;
            }

            sector = imagePlugin.ReadSectors(lvd.integritySequenceExtent.location * ratio, ratio);
            lvid   = Marshal.ByteArrayToStructureLittleEndian<LogicalVolumeIntegrityDescriptor>(sector);

            if(lvid.tag.tagIdentifier == TagIdentifier.LogicalVolumeIntegrityDescriptor &&
               lvid.tag.tagLocation   == lvd.integritySequenceExtent.location)
                lvidiu =
                    Marshal.ByteArrayToStructureLittleEndian<LogicalVolumeIntegrityDescriptorImplementationUse>(sector,
                        (int)((lvid.numberOfPartitions * 8) + 80),
                        System.Runtime.InteropServices.Marshal.SizeOf(lvidiu));
            else
                lvid = new LogicalVolumeIntegrityDescriptor();

            sbInformation.AppendFormat("Volume is number {0} of {1}", pvd.volumeSequenceNumber,
                                       pvd.maximumVolumeSequenceNumber).AppendLine();

            sbInformation.AppendFormat("Volume set identifier: {0}",
                                       StringHandlers.DecompressUnicode(pvd.volumeSetIdentifier)).AppendLine();

            sbInformation.
                AppendFormat("Volume name: {0}", StringHandlers.DecompressUnicode(lvd.logicalVolumeIdentifier)).
                AppendLine();

            sbInformation.AppendFormat("Volume uses {0} bytes per block", lvd.logicalBlockSize).AppendLine();

            sbInformation.AppendFormat("Volume was last written in {0}", EcmaToDateTime(lvid.recordingDateTime)).
                          AppendLine();

            sbInformation.AppendFormat("Volume contains {0} partitions", lvid.numberOfPartitions).AppendLine();

            sbInformation.
                AppendFormat("Volume contains {0} files and {1} directories", lvidiu.files, lvidiu.directories).
                AppendLine();

            sbInformation.AppendFormat("Volume conforms to {0}",
                                       Encoding.GetString(lvd.domainIdentifier.identifier).TrimEnd('\u0000')).
                          AppendLine();

            sbInformation.AppendFormat("Volume was last written by: {0}",
                                       Encoding.GetString(pvd.implementationIdentifier.identifier).TrimEnd('\u0000')).
                          AppendLine();

            sbInformation.AppendFormat("Volume requires UDF version {0}.{1:X2} to be read",
                                       Convert.ToInt32($"{(lvidiu.minimumReadUDF & 0xFF00) >> 8}", 10),
                                       Convert.ToInt32($"{lvidiu.minimumReadUDF & 0xFF}", 10)).AppendLine();

            sbInformation.AppendFormat("Volume requires UDF version {0}.{1:X2} to be written to",
                                       Convert.ToInt32($"{(lvidiu.minimumWriteUDF & 0xFF00) >> 8}", 10),
                                       Convert.ToInt32($"{lvidiu.minimumWriteUDF & 0xFF}", 10)).AppendLine();

            sbInformation.AppendFormat("Volume cannot be written by any UDF version higher than {0}.{1:X2}",
                                       Convert.ToInt32($"{(lvidiu.maximumWriteUDF & 0xFF00) >> 8}", 10),
                                       Convert.ToInt32($"{lvidiu.maximumWriteUDF & 0xFF}", 10)).AppendLine();

            XmlFsType = new FileSystemType
            {
                Type =
                    $"UDF v{Convert.ToInt32($"{(lvidiu.maximumWriteUDF & 0xFF00) >> 8}", 10)}.{Convert.ToInt32($"{lvidiu.maximumWriteUDF & 0xFF}", 10):X2}",
                ApplicationIdentifier = Encoding.GetString(pvd.implementationIdentifier.identifier).TrimEnd('\u0000'),
                ClusterSize = lvd.logicalBlockSize,
                ModificationDate = EcmaToDateTime(lvid.recordingDateTime),
                ModificationDateSpecified = true,
                Files = lvidiu.files,
                FilesSpecified = true,
                VolumeName = StringHandlers.DecompressUnicode(lvd.logicalVolumeIdentifier),
                VolumeSetIdentifier = StringHandlers.DecompressUnicode(pvd.volumeSetIdentifier),
                VolumeSerial = StringHandlers.DecompressUnicode(pvd.volumeSetIdentifier),
                SystemIdentifier = Encoding.GetString(pvd.implementationIdentifier.identifier).TrimEnd('\u0000')
            };

            XmlFsType.Clusters = (partition.End - partition.Start + 1) * imagePlugin.Info.SectorSize /
                                 XmlFsType.ClusterSize;

            information = sbInformation.ToString();
        }