/// <summary> /// Reads data from the file. /// </summary> /// <param name="volumeIndex">The volume index of the file.</param> /// <param name="item">The item containing information about the file to read.</param> /// <param name="buffer">The buffer to read the data</param> /// <returns>Returns true if the data was read successfully.</returns> private bool ReadFromFile(int volumeIndex, ref UdfRecord item, ref byte[] buffer) { if (item.Size >= MaxExtents) { return(false); } if (item.IsInline) { buffer = item.InlineData; return(true); } buffer = new byte[item.Size]; int position = 0; for (int i = 0; i < item.Extents.Count; i++) { FileExtent e = item.Extents[i]; int length = e.Length; byte[] b = UdfHelper.Readbytes(position, buffer, buffer.Length); if (!this.ReadData(volumeIndex, e.PartitionReference, e.Position, length, b)) { return(false); } position += length; } return(true); }
/// <summary> /// Reads a record from the file system. /// </summary> /// <param name="item">The item to read the data into.</param> /// <param name="volumeIndex">The index of the volume the file resides on.</param> /// <param name="lad">The long allocation descriptor of the file.</param> /// <param name="numRecurseAllowed">The number of recursions allowed before the method fails.</param> /// <returns>Returns true if the item and all sub items were read correctly.</returns> private bool ReadRecord(UdfRecord item, int volumeIndex, LongAllocationDescriptor lad, int numRecurseAllowed) { if (numRecurseAllowed-- == 0) { return(false); } LogicalVolume vol = this.LogicalVolumes[volumeIndex]; Partition partition = this.Partitions[vol.PartitionMaps[lad.Location.PartitionReference].PartitionIndex]; int key = lad.Location.Position; if (partition.Map.ContainsKey(key)) { // Item already in the map, just look it up instead of reading it from the image. item.VolumeIndex = partition.Map[key].VolumeIndex; item.PartitionIndex = partition.Map[key].PartitionIndex; item.Extents = partition.Map[key].Extents; item._size = partition.Map[key]._size; item.Key = key; } else { item.VolumeIndex = volumeIndex; item.PartitionIndex = vol.PartitionMaps[lad.Location.PartitionReference].PartitionIndex; item.Key = key; if (!partition.Map.Set(key, item) || !this.ReadRecordData(item, volumeIndex, lad, numRecurseAllowed)) { return(false); } } return(true); }
/// <summary> /// Validates to ensure all item extents are in a valid format. /// </summary> /// <param name="volumeIndex">The volume index.</param> /// <param name="item">The item to validate.</param> /// <returns>Returns true if the extents are valid.</returns> private bool CheckItemExtents(int volumeIndex, UdfRecord item) { foreach (FileExtent extent in item.Extents) { if (!this.CheckExtent(volumeIndex, extent.PartitionReference, extent.Position, extent.Length)) { return(false); } } return(true); }
/// <summary> /// Reads the data from the image for a record. /// </summary> /// <param name="item">The item to read the data into.</param> /// <param name="volumeIndex">The index of the volume the file resides on.</param> /// <param name="lad">The long allocation descriptor of the file.</param> /// <param name="numRecurseAllowed">The number of recursions allowed before the method fails.</param> /// <returns>Returns true if the record was read successfully.</returns> private bool ReadRecordData(UdfRecord item, int volumeIndex, LongAllocationDescriptor lad, int numRecurseAllowed) { if (this.itemCount > MaxItems) { return(false); } LogicalVolume volume = this.LogicalVolumes[volumeIndex]; if (lad.Length != volume.BlockSize) { return(false); } // Read the record. int size = lad.Length; byte[] buffer = new byte[size]; if (!this.ReadData(volumeIndex, lad, buffer)) { return(false); } // Validate the tag is a file. VolumeTag tag = new VolumeTag(); tag.Parse(0, buffer, size); if (tag.Identifier != (short)VolumeDescriptorType.File) { return(false); } // Validate the IcbTage indicates file or directory. item.IcbTag.Parse(16, buffer); if (item.IcbTag.FileType != IcbFileType.Directory && item.IcbTag.FileType != IcbFileType.File) { return(false); } item.Parse(buffer); int extendedAttrLen = UdfHelper.Get32(168, buffer); int allocDescriptorsLen = UdfHelper.Get32(172, buffer); if ((extendedAttrLen & 3) != 0) { return(false); } int position = 176; if (extendedAttrLen > size - position) { return(false); } position += extendedAttrLen; IcbDescriptorType desctType = item.IcbTag.DescriptorType; if (allocDescriptorsLen > size - position) { return(false); } if (desctType == IcbDescriptorType.Inline) { // If the file data is inline, read it in now since we have it. item.IsInline = true; item.InlineData = UdfHelper.Readbytes(position, buffer, allocDescriptorsLen); } else { // Otherwise read the information about where the file is located for later. item.IsInline = false; item.InlineData = new byte[0]; if ((desctType != IcbDescriptorType.Short) && (desctType != IcbDescriptorType.Long)) { return(false); } for (int index = 0; index < allocDescriptorsLen;) { FileExtent extent = new FileExtent(); if (desctType == IcbDescriptorType.Short) { if (index + 8 > allocDescriptorsLen) { return(false); } ShortAllocationDescriptor sad = new ShortAllocationDescriptor(); sad.Parse(position + index, buffer); extent.Position = sad.Position; extent.Length = sad.Length; extent.PartitionReference = lad.Location.PartitionReference; index += 8; } else { if (index + 16 > allocDescriptorsLen) { return(false); } LongAllocationDescriptor ladNew = new LongAllocationDescriptor(); ladNew.Parse(position + index, buffer); extent.Position = ladNew.Location.Position; extent.PartitionReference = ladNew.Location.PartitionReference; extent.Length = ladNew.Length; index += 16; } item.Extents.Add(extent); } } if (item.IcbTag.IsDirectory) { if (!item.CheckChunkSizes() || !this.CheckItemExtents(volumeIndex, item)) { return(false); } buffer = new byte[0]; if (!this.ReadFromFile(volumeIndex, ref item, ref buffer)) { return(false); } item._size = 0; item.Extents.Clear(); size = buffer.Length; int processedTotal = 0; int processedCur = -1; while (processedTotal < size || processedCur == 0) { UdfFileInformation fileId = new UdfFileInformation(); fileId.Parse(processedTotal, buffer, (size - processedTotal), ref processedCur); if (!fileId.IsItLinkParent) { // Recursively read the contentst of the drirectory UdfRecord fileItem = new UdfRecord(); fileItem.Id = fileId.Identifier; if (fileItem.Id.Data != null) { this.fileNameLengthTotal += fileItem.Id.Data.Length; } if (this.fileNameLengthTotal > MaxFileNameLength) { return(false); } fileItem._Parent = item; item.Subitems.Add(fileItem); if (item.Subitems.Count > MaxFiles) { return(false); } this.ReadRecord(fileItem, volumeIndex, fileId.Icb, numRecurseAllowed); } processedTotal += processedCur; } } else { if (item.Extents.Count > MaxExtents - this.numExtents) { return(false); } this.numExtents += item.Extents.Count; if (item.InlineData.Length > MaxInlineExtentsSize - this.inlineExtentsSize) { return(false); } this.inlineExtentsSize += item.InlineData.Length; } this.itemCount++; return(true); }
/// <summary> /// Extracts an individual record from the image to the given directory. /// </summary> /// <param name="path">The directory to extract the record to.</param> /// <param name="record">The record to extract.</param> private void Extract(string path, ImageRecord record) { if (string.IsNullOrEmpty(path)) { throw new ArgumentNullException("path"); } if (record == null) { throw new ArgumentNullException("record"); } // Only extract UDF records. if (!record.IsUdf) { return; } string target = Path.Combine(path, record.Name); if (record.IsDirectory || String.IsNullOrEmpty(record.Name)) { Directory.CreateDirectory(target); // No sub items for this directory, continue. if (record.Subitems == null || record.Subitems.Count <= 0) { return; } for (int i = 0; i < record.Subitems.Count; i++) { if (this.WorkerThread.CancellationPending) { break; // Canceled, exit loop } this.Extract(target, record.Subitems[i]); } } else { UdfRecord item = (UdfRecord)record; if (item.IsRecAndAlloc() && item.CheckChunkSizes() && this.CheckItemExtents(item.VolumeIndex, item)) { if (item.IsInline) { if (item.InlineData != null) { if (item.InlineData.Length == 0) { return; } File.WriteAllBytes(target, item.InlineData); } } else { Partition part = this.Partitions[item.PartitionIndex]; this.currentBlockSize = this.LogicalVolumes[item.VolumeIndex].BlockSize; long logBlockNumber = item.Key; if (item.Extents.Count > 0) { logBlockNumber = item.Extents[0].Position; } long start = ((long)part.Position << SectorSizeLog) + (logBlockNumber * this.currentBlockSize); this.Extract(target, start, item.Size); } } } }
/// <summary> /// Reads the data from the image for a record. /// </summary> /// <param name="item">The item to read the data into.</param> /// <param name="volumeIndex">The index of the volume the file resides on.</param> /// <param name="lad">The long allocation descriptor of the file.</param> /// <param name="numRecurseAllowed">The number of recursions allowed before the method fails.</param> /// <returns>Returns true if the record was read successfully.</returns> private bool ReadRecordData(UdfRecord item, int volumeIndex, LongAllocationDescriptor lad, int numRecurseAllowed) { if (this.itemCount > MaxItems) { return false; } LogicalVolume volume = this.LogicalVolumes[volumeIndex]; if (lad.Length != volume.BlockSize) { return false; } // Read the record. int size = lad.Length; byte[] buffer = new byte[size]; if (!this.ReadData(volumeIndex, lad, buffer)) { return false; } // Validate the tag is a file. VolumeTag tag = new VolumeTag(); tag.Parse(0, buffer, size); if (tag.Identifier != (short)VolumeDescriptorType.File) { return false; } // Validate the IcbTage indicates file or directory. item.IcbTag.Parse(16, buffer); if (item.IcbTag.FileType != IcbFileType.Directory && item.IcbTag.FileType != IcbFileType.File) { return false; } item.Parse(buffer); int extendedAttrLen = UdfHelper.Get32(168, buffer); int allocDescriptorsLen = UdfHelper.Get32(172, buffer); if ((extendedAttrLen & 3) != 0) { return false; } int position = 176; if (extendedAttrLen > size - position) { return false; } position += extendedAttrLen; IcbDescriptorType desctType = item.IcbTag.DescriptorType; if (allocDescriptorsLen > size - position) { return false; } if (desctType == IcbDescriptorType.Inline) { // If the file data is inline, read it in now since we have it. item.IsInline = true; item.InlineData = UdfHelper.Readbytes(position, buffer, allocDescriptorsLen); } else { // Otherwise read the information about where the file is located for later. item.IsInline = false; item.InlineData = new byte[0]; if ((desctType != IcbDescriptorType.Short) && (desctType != IcbDescriptorType.Long)) { return false; } for (int index = 0; index < allocDescriptorsLen;) { FileExtent extent = new FileExtent(); if (desctType == IcbDescriptorType.Short) { if (index + 8 > allocDescriptorsLen) { return false; } ShortAllocationDescriptor sad = new ShortAllocationDescriptor(); sad.Parse(position + index, buffer); extent.Position = sad.Position; extent.Length = sad.Length; extent.PartitionReference = lad.Location.PartitionReference; index += 8; } else { if (index + 16 > allocDescriptorsLen) { return false; } LongAllocationDescriptor ladNew = new LongAllocationDescriptor(); ladNew.Parse(position + index, buffer); extent.Position = ladNew.Location.Position; extent.PartitionReference = ladNew.Location.PartitionReference; extent.Length = ladNew.Length; index += 16; } item.Extents.Add(extent); } } if (item.IcbTag.IsDirectory) { if (!item.CheckChunkSizes() || !this.CheckItemExtents(volumeIndex, item)) { return false; } buffer = new byte[0]; if (!this.ReadFromFile(volumeIndex, ref item, ref buffer)) { return false; } item._size = 0; item.Extents.Clear(); size = buffer.Length; int processedTotal = 0; int processedCur = -1; while (processedTotal < size || processedCur == 0) { UdfFileInformation fileId = new UdfFileInformation(); fileId.Parse(processedTotal, buffer, (size - processedTotal), ref processedCur); if (!fileId.IsItLinkParent) { // Recursively read the contentst of the drirectory UdfRecord fileItem = new UdfRecord(); fileItem.Id = fileId.Identifier; if (fileItem.Id.Data != null) { this.fileNameLengthTotal += fileItem.Id.Data.Length; } if (this.fileNameLengthTotal > MaxFileNameLength) { return false; } fileItem._Parent = item; item.Subitems.Add(fileItem); if (item.Subitems.Count > MaxFiles) { return false; } this.ReadRecord(fileItem, volumeIndex, fileId.Icb, numRecurseAllowed); } processedTotal += processedCur; } } else { if (item.Extents.Count > MaxExtents - this.numExtents) { return false; } this.numExtents += item.Extents.Count; if (item.InlineData.Length > MaxInlineExtentsSize - this.inlineExtentsSize) { return false; } this.inlineExtentsSize += item.InlineData.Length; } this.itemCount++; return true; }
/// <summary> /// Reads a record from the file system. /// </summary> /// <param name="item">The item to read the data into.</param> /// <param name="volumeIndex">The index of the volume the file resides on.</param> /// <param name="lad">The long allocation descriptor of the file.</param> /// <param name="numRecurseAllowed">The number of recursions allowed before the method fails.</param> /// <returns>Returns true if the item and all sub items were read correctly.</returns> private bool ReadRecord(UdfRecord item, int volumeIndex, LongAllocationDescriptor lad, int numRecurseAllowed) { if (numRecurseAllowed-- == 0) { return false; } LogicalVolume vol = this.LogicalVolumes[volumeIndex]; Partition partition = this.Partitions[vol.PartitionMaps[lad.Location.PartitionReference].PartitionIndex]; int key = lad.Location.Position; if (partition.Map.ContainsKey(key)) { // Item already in the map, just look it up instead of reading it from the image. item.VolumeIndex = partition.Map[key].VolumeIndex; item.PartitionIndex = partition.Map[key].PartitionIndex; item.Extents = partition.Map[key].Extents; item._size = partition.Map[key]._size; item.Key = key; } else { item.VolumeIndex = volumeIndex; item.PartitionIndex = vol.PartitionMaps[lad.Location.PartitionReference].PartitionIndex; item.Key = key; if (!partition.Map.Set(key, item) || !this.ReadRecordData(item, volumeIndex, lad, numRecurseAllowed)) { return false; } } return true; }
/// <summary> /// Reads data from the file. /// </summary> /// <param name="volumeIndex">The volume index of the file.</param> /// <param name="item">The item containing information about the file to read.</param> /// <param name="buffer">The buffer to read the data</param> /// <returns>Returns true if the data was read successfully.</returns> private bool ReadFromFile(int volumeIndex, ref UdfRecord item, ref byte[] buffer) { if (item.Size >= MaxExtents) { return false; } if (item.IsInline) { buffer = item.InlineData; return true; } buffer = new byte[item.Size]; int position = 0; for (int i = 0; i < item.Extents.Count; i++) { FileExtent e = item.Extents[i]; int length = e.Length; byte[] b = UdfHelper.Readbytes(position, buffer, buffer.Length); if (!this.ReadData(volumeIndex, e.PartitionReference, e.Position, length, b)) { return false; } position += length; } return true; }
/// <summary> /// Validates to ensure all item extents are in a valid format. /// </summary> /// <param name="volumeIndex">The volume index.</param> /// <param name="item">The item to validate.</param> /// <returns>Returns true if the extents are valid.</returns> private bool CheckItemExtents(int volumeIndex, UdfRecord item) { foreach (FileExtent extent in item.Extents) { if (!this.CheckExtent(volumeIndex, extent.PartitionReference, extent.Position, extent.Length)) { return false; } } return true; }