private static unsafe void _Dump(NtfsAttributeListAttribute *from, DumpCallbackDelegate callback) { if (null == from) { throw new ArgumentNullException(); } if (NtfsAttributeType.AttributeAttributeList != from->Header.AttributeType) { throw new ArgumentException(); } IPartitionClusterData disposableData = null; ListEntry * listBase = null; uint listLength; try { if (from->Header.IsResident) { NtfsResidentAttribute *listAttribute = (NtfsResidentAttribute *)from; listBase = (ListEntry *)((byte *)from + listAttribute->ValueOffset); listLength = listAttribute->ValueLength; } else { NtfsNonResidentAttribute *listAttribute = (NtfsNonResidentAttribute *)from; disposableData = listAttribute->GetData(); if (null == disposableData) { throw new ApplicationException(); } listBase = (ListEntry *)disposableData.Data; ulong candidateLength = listAttribute->DataSize; if (uint.MaxValue < candidateLength) { throw new ApplicationException(); } listLength = (uint)candidateLength; } if (null == listBase) { throw new ApplicationException(); } uint offset = 0; while (offset < listLength) { ListEntry *entry = (ListEntry *)((byte *)listBase + offset); callback(entry); offset += entry->EntryLength; } } finally { if (null != disposableData) { disposableData.Dispose(); } } }
public static void InsertHeadList( ListEntry *ListHead, ListEntry *Entry ) { ListEntry *flink; flink = ListHead->Flink; Entry->Flink = flink; Entry->Blink = ListHead; flink->Blink = Entry; ListHead->Flink = Entry; }
public static void InsertTailList( ListEntry *ListHead, ListEntry *Entry ) { ListEntry *blink; blink = ListHead->Blink; Entry->Flink = ListHead; Entry->Blink = blink; blink->Flink = Entry; ListHead->Blink = Entry; }
public static ListEntry *RemoveTailList( ListEntry *ListHead ) { ListEntry *blink; ListEntry *entry; entry = ListHead->Blink; blink = entry->Blink; ListHead->Blink = blink; blink->Flink = ListHead; return(entry); }
public static ListEntry *RemoveHeadList( ListEntry *ListHead ) { ListEntry *flink; ListEntry *entry; entry = ListHead->Flink; flink = entry->Flink; ListHead->Flink = flink; flink->Blink = ListHead; return(entry); }
public static bool RemoveEntryList( ListEntry *Entry ) { ListEntry *blink; ListEntry *flink; flink = Entry->Flink; blink = Entry->Blink; blink->Flink = flink; flink->Blink = blink; return(flink == blink); }
public static bool IsListEmpty( ListEntry *ListHead ) { return(ListHead->Flink == ListHead->Blink); }
public static void InitializeListHead( ListEntry *ListHead ) { ListHead->Flink = ListHead->Blink = ListHead; }
/// <summary>The caller enumerating entries might be interested in the content of /// the currently scanned entry. This might be either for additional filtering at /// attribute level (including attribute name) or for full attribute data processing. /// </summary> /// <param name="entry">The scanned list entry of interest. Should a single /// attribute span several entries, this one is guaranteed to be the first one for the /// attribute.</param> /// <param name="remainingBytesInList">Number of bytes remaining in list, relatively to the /// entry address.</param> /// <param name="entryReferencedAttributeHandler">The callback that will be invoked /// with the referenced attribute with or without full attribute data depending on /// the value of <paramref name="dataIncluded"/></param> /// <param name="dataIncluded"></param> /// <returns>true if caller should continue process data, false if it should stop.</returns> private static unsafe bool HandleEntryReferencedAttribute(ListEntry *entry, uint remainingBytesInList, EntryListReferencedAttributeHandlerDelegate entryReferencedAttributeHandler, bool dataIncluded) { ushort currentAttributeNumber = entry->AttributeNumber; NtfsAttributeType currentAttributeType = entry->AttributeType; byte * baseAddress = (byte *)entry; uint relativeOffset = 0; // The last callback invocation decided it needs some more data before deciding // what to do. IPartitionClusterData clusterData = null; NtfsPartition currentPartition = NtfsPartition.Current; using (PartitionDataDisposableBatch batch = PartitionDataDisposableBatch.CreateNew()) { while (true) { ListEntry * scannedEntry = (ListEntry *)baseAddress; ulong mainFileReferenceNumber = entry->FileReferenceNumber; List <ulong> entries = new List <ulong>(); Stream dataStream = null; if (dataIncluded) { // Read each record and prepare for data retrieval. while (true) { entries.Add(scannedEntry->FileReferenceNumber); relativeOffset += scannedEntry->EntryLength; if (relativeOffset >= remainingBytesInList) { // Take care not to go further than the end of the list. break; } scannedEntry = (ListEntry *)(baseAddress + relativeOffset); if ((currentAttributeNumber != scannedEntry->AttributeNumber) || (currentAttributeType != scannedEntry->AttributeType)) { break; } } dataStream = new MultiRecordAttributeDataStream(entries); } // Retrieve attribute itself. NtfsFileRecord *mainFileRecord = currentPartition.GetFileRecord(mainFileReferenceNumber, ref clusterData); if (null == mainFileRecord) { throw new ApplicationException(); } NtfsAttribute *retrievedAttribute = (NtfsAttribute *)((byte *)mainFileRecord + mainFileRecord->AttributesOffset); // Invoke callback. bool retry; if (!entryReferencedAttributeHandler(retrievedAttribute, dataStream, out retry)) { // After attribute has been processed, it has been decided no other list // entry should be performed. return(false); } if (!retry) { // After attribute has been processed, it has been decided that no // additional data from this attribute is required. However the enumeration // of other list entries should continue. return(true); } if (dataIncluded) { throw new InvalidOperationException(); } // Attribute has been processed, however not enough data was available for a // final decision. We loop and include all data now. dataIncluded = true; } } }
/// <summary></summary> /// <param name="from">The NtfsAttributeListAttribute to be used for enumeration.</param> /// <param name="searchedAttributeType">The type of the searched attribute or /// <see cref="NtfsAttributeType.Any"/> if the caller is interested in all kinds of /// attributes.</param> /// <param name="listEntryHandler">A callback to be invoked on each entry matching /// the attribute type selection criteria.</param> /// <remarks>WARNING : This might seems counterintuitive to have this method at a class /// level instead of making it an instance one. This is because we absolutely don't want /// it to be invoked on an object reference that is subject to being moved in memory by /// the GC. Forcing the caller to provide a pointer makes her responsible for enforcing /// the pinning requirements.</remarks> internal static unsafe void EnumerateEntries(NtfsAttribute *from, NtfsAttributeType searchedAttributeType, EntryEnumeratorCallbackDelegate listEntryHandler) { if (null == from) { throw new ArgumentNullException(); } if (NtfsAttributeType.AttributeAttributeList != from->AttributeType) { throw new ArgumentException(); } IPartitionClusterData listAttributeData = null; // Address of first ListeEntry item for this attribute. ListEntry *listBase = null; uint listLength; try { if (from->IsResident) { NtfsResidentAttribute *listAttribute = (NtfsResidentAttribute *)from; listBase = (ListEntry *)((byte *)from + listAttribute->ValueOffset); listLength = listAttribute->ValueLength; } else { NtfsNonResidentAttribute *listAttribute = (NtfsNonResidentAttribute *)from; listAttributeData = listAttribute->GetData(); if (null == listAttributeData) { throw new ApplicationException(); } listBase = (ListEntry *)listAttributeData.Data; ulong candidateLength = listAttribute->DataSize; if (uint.MaxValue < candidateLength) { throw new ApplicationException(); } listLength = (uint)candidateLength; } if (null == listBase) { throw new ApplicationException(); } NtfsAttributeType currentAttributeType = NtfsAttributeType.Any; ushort currentAttributeNumber = ushort.MaxValue; ListEntry * scannedEntry; for (uint offset = 0; offset < listLength; offset += scannedEntry->EntryLength) { scannedEntry = (ListEntry *)((byte *)listBase + offset); if ((currentAttributeNumber == scannedEntry->AttributeNumber) && (currentAttributeType == scannedEntry->AttributeType)) { // The entry is a continuation of the previous one. Ignore it. It should // have been processed by a previous loop if required. continue; } currentAttributeNumber = scannedEntry->AttributeNumber; currentAttributeType = scannedEntry->AttributeType; if ((NtfsAttributeType.Any != searchedAttributeType) && (scannedEntry->AttributeType != searchedAttributeType)) { // This entry doesn't match the search criteria on attribute type. continue; } EntryListReferencedAttributeHandlerDelegate attributeDataHandler; bool includeData; if (!listEntryHandler(scannedEntry, out attributeDataHandler, out includeData)) { // The callback doesn't wish to continue with other list entries. return; } if (null == attributeDataHandler) { // The callback doesn't wish to retrieve the attribute itself for the // currently scanned entry. continue; } // The last callback invocation decided it needs some data from the attribute // itself before deciding what to do. if (!HandleEntryReferencedAttribute(scannedEntry, listLength - offset, attributeDataHandler, includeData)) { return; } } } finally { if (null != listAttributeData) { listAttributeData.Dispose(); } } }