diff --git a/Src/DSInternals.DataStore/DirectoryContext.cs b/Src/DSInternals.DataStore/DirectoryContext.cs index f2da7b1..039010c 100644 --- a/Src/DSInternals.DataStore/DirectoryContext.cs +++ b/Src/DSInternals.DataStore/DirectoryContext.cs @@ -51,9 +51,10 @@ // TODO: Add param explanations isamParameters.LogFileSize = ADConstants.EseLogFileSize; isamParameters.DeleteOutOfRangeLogs = true; + isamParameters.EnableIndexChecking = 1; + isamParameters.EnableIndexCleanup = true; isamParameters.CircularLog = true; // TODO: Configure additional ISAM parameters - // this.instance.IsamSystemParameters.EnableIndexChecking = true; // this.instance.IsamSystemParameters.EnableOnlineDefrag = false; // JET_paramDeleteOldLogs = 1 this.session = this.instance.CreateSession(); diff --git a/Src/DSInternals.DataStore/DirectorySchema.cs b/Src/DSInternals.DataStore/DirectorySchema.cs index 937d931..1a956e9 100644 --- a/Src/DSInternals.DataStore/DirectorySchema.cs +++ b/Src/DSInternals.DataStore/DirectorySchema.cs @@ -33,11 +33,10 @@ this.LoadAttributeIndices(dataTable.Indices); using (var cursor = database.OpenCursor(ADConstants.DataTableName)) { - this.LoadAttributeProperties(cursor); this.LoadClassList(cursor); + this.LoadAttributeProperties(cursor); this.LoadPrefixMap(cursor); } - // TODO: Load Ext-Int Map from hiddentable } @@ -204,19 +203,20 @@ Columnid isDefunctCol = this.LoadColumnIdByAttributeName(dataTableCursor, CommonDirectoryAttributes.IsDefunct); // Now traverse through all schema attributes and load their properties - dataTableCursor.CurrentIndex = this.attributesByInternalId[CommonDirectoryAttributes.ObjectClassId].Index; - dataTableCursor.FindRecords(MatchCriteria.EqualTo, Key.Compose(CommonDirectoryClasses.AttributeSchemaId)); + // Use this filter: (objectCategory=attributeSchema) + dataTableCursor.CurrentIndex = this.attributesByInternalId[CommonDirectoryAttributes.ObjectCategoryId].Index; + dataTableCursor.FindRecords(MatchCriteria.EqualTo, Key.Compose(this.FindClassId(CommonDirectoryClasses.AttributeSchema))); while (dataTableCursor.MoveNext()) { int? internalId = dataTableCursor.RetrieveColumnAsInt(internalIdCol); int attributeId = dataTableCursor.RetrieveColumnAsInt(attributeIdCol).Value; - // Some built-in attributes do not have internal id set, which meand it is equal to the public id + // Some built-in attributes do not have internal id set, which means it is equal to the public id int id = internalId ?? attributeId; SchemaAttribute attribute; bool found = this.attributesByInternalId.TryGetValue(id, out attribute); if (! found) { - // Load info about a new attribute + // We are loading info about a new attribute attribute = new SchemaAttribute(); attribute.InternalId = internalId; } @@ -254,25 +254,32 @@ // Initialize the class list this.classesByName = new Dictionary(); - // Load column IDs - Columnid ldapDisplayNameCol = this.FindColumnId(CommonDirectoryAttributes.LDAPDisplayName); + // Load column IDs. We are in an early stage of schema loading, which means that we cannot search for non-system attributes by name. Columnid dntCol = this.FindColumnId(CommonDirectoryAttributes.DNTag); + Columnid governsIdCol = this.attributesByInternalId[CommonDirectoryAttributes.GovernsIdId].ColumnID; + SchemaAttribute ldapDisplayNameAtt = this.attributesByInternalId[CommonDirectoryAttributes.LdapDisplayNameId]; - // Search for all classes - dataTableCursor.CurrentIndex = this.FindIndexName(CommonDirectoryAttributes.ObjectClass); - dataTableCursor.FindRecords(MatchCriteria.EqualTo, Key.Compose(CommonDirectoryClasses.ClassSchemaId)); + // Search for all classes using this heuristics: (&(ldapDisplayName=*)(governsId=*)) + dataTableCursor.CurrentIndex = ldapDisplayNameAtt.Index; while (dataTableCursor.MoveNext()) { + int? governsId = dataTableCursor.RetrieveColumnAsInt(governsIdCol); + if(!governsId.HasValue) + { + // This is an attribute and not a class, so we skip to the next object. + continue; + } // TODO: Load more data about classes - int classId = dataTableCursor.RetrieveColumnAsDNTag(dntCol).Value; - string className = dataTableCursor.RetrieveColumnAsString(ldapDisplayNameCol); - classesByName.Add(className, classId); + int classDNT = dataTableCursor.RetrieveColumnAsDNTag(dntCol).Value; + string className = dataTableCursor.RetrieveColumnAsString(ldapDisplayNameAtt.ColumnID); + classesByName.Add(className, classDNT); } } private void LoadPrefixMap(Cursor dataTableCursor) { - // Find the Schema Naming Context by its objectCategory + // Find the Schema Naming Context using this filter: (objectCategory=dMD) + dataTableCursor.FindAllRecords(); dataTableCursor.CurrentIndex = this.FindIndexName(CommonDirectoryAttributes.ObjectCategory); int schemaObjectCategoryId = this.FindClassId(CommonDirectoryClasses.Schema); bool schemaFound = dataTableCursor.GotoKey(Key.Compose(schemaObjectCategoryId)); diff --git a/Src/DSInternals.DataStore/DomainController.cs b/Src/DSInternals.DataStore/DomainController.cs index a24c8d6..df4ea74 100644 --- a/Src/DSInternals.DataStore/DomainController.cs +++ b/Src/DSInternals.DataStore/DomainController.cs @@ -46,12 +46,19 @@ this.NTDSSettingsDNT = this.systemTableCursor.RetrieveColumnAsInt(ntdsSettingsCol).Value; if(this.systemTableCursor.TableDefinition.Columns.Contains(osVersionMajorCol)) { - // Some databases like the initial adamntds.dit do not contain the OS Version + // Some databases like the initial adamntds.dit or ntds.dit on Windows Server 2003 do not contain the OS Version this.OSVersionMinor = this.systemTableCursor.RetrieveColumnAsUInt(osVersionMinorCol); this.OSVersionMajor = this.systemTableCursor.RetrieveColumnAsUInt(osVersionMajorCol); } - this.epochCache = this.systemTableCursor.RetrieveColumnAsInt(epochCol); - this.UsnAtIfm = this.systemTableCursor.RetrieveColumnAsLong(usnAtIfmCol); + if (this.systemTableCursor.TableDefinition.Columns.Contains(epochCol)) + { + // This is a new feature since Windows Server 2008 + this.epochCache = this.systemTableCursor.RetrieveColumnAsInt(epochCol); + } + if (this.systemTableCursor.TableDefinition.Columns.Contains(usnAtIfmCol)) + { + this.UsnAtIfm = this.systemTableCursor.RetrieveColumnAsLong(usnAtIfmCol); + } this.BackupExpiration = this.systemTableCursor.RetrieveColumnAsGeneralizedTime(backupExpirationCol); this.BackupUsn = this.systemTableCursor.RetrieveColumnAsLong(backupUsnCol); this.State = (DatabaseState) this.systemTableCursor.RetrieveColumnAsInt(stateCol).Value; @@ -212,6 +219,12 @@ } set { + if(this.epochCache == null) + { + // This is a legacy DB without the epoch_col, so we cannot change it. + // TODO: Extract as a resource. + throw new InvalidOperationException("Current database does not support epoch information."); + } // Update table this.systemTableCursor.BeginEditForUpdate(); this.systemTableCursor.EditRecord[epochCol] = value;