/// <summary> /// Creates a new Active Directory database context. /// </summary> /// <param name="dbPath">dbFilePath must point to the DIT file on the local computer.</param> /// <param name="logPath">The path should point to a writeable folder on the local computer, where ESE log files will be created. If not specified, then temp folder will be used.</param> public DirectoryContext(string dbFilePath, bool readOnly, string logDirectoryPath = null) { if (!File.Exists(dbFilePath)) { // TODO: Extract as resource throw new FileNotFoundException("The specified database file does not exist.", dbFilePath); } ValidateDatabaseState(dbFilePath); string dbDirectoryPath = Path.GetDirectoryName(dbFilePath); string checkpointDirectoryPath = dbDirectoryPath; string tempDirectoryPath = dbDirectoryPath; if (logDirectoryPath != null) { if (!Directory.Exists(logDirectoryPath)) { // TODO: Extract as resource throw new FileNotFoundException("The specified log directory does not exist.", logDirectoryPath); } } else { logDirectoryPath = dbDirectoryPath; } // TODO: Exception handling? // HACK: IsamInstance constructor throws AccessDenied Exception when the path does not end with a backslash. this.instance = new IsamInstance(AddPathSeparator(checkpointDirectoryPath), AddPathSeparator(logDirectoryPath), AddPathSeparator(tempDirectoryPath), ADConstants.EseBaseName, JetInstanceName, readOnly, ADConstants.PageSize); try { var isamParameters = this.instance.IsamSystemParameters; // 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.EnableOnlineDefrag = false; // JET_paramDeleteOldLogs = 1 this.session = this.instance.CreateSession(); this.session.AttachDatabase(dbFilePath); this.attachedDatabasePath = dbFilePath; this.database = this.session.OpenDatabase(dbFilePath); this.Schema = new DirectorySchema(this.database); this.SecurityDescriptorRersolver = new SecurityDescriptorRersolver(this.database); this.DistinguishedNameResolver = new DistinguishedNameResolver(this.database, this.Schema); this.LinkResolver = new LinkResolver(this.database, this.Schema); this.DomainController = new DomainController(this); } catch { // Free resources if anything failed this.Dispose(); throw; } }
protected virtual void Dispose(bool disposing) { if (!disposing) { // Do nothing return; } if (this.LinkResolver != null) { this.LinkResolver.Dispose(); this.LinkResolver = null; } if (this.SecurityDescriptorRersolver != null) { this.SecurityDescriptorRersolver.Dispose(); this.SecurityDescriptorRersolver = null; } if (this.DistinguishedNameResolver != null) { this.DistinguishedNameResolver.Dispose(); this.DistinguishedNameResolver = null; } if (this.DomainController != null) { this.DomainController.Dispose(); this.DomainController = null; } if (this.database != null) { this.database.Dispose(); this.database = null; } if (this.session != null) { if (this.isDBAttached) { this.session.DetachDatabase(this.DSADatabaseFile); this.isDBAttached = false; } this.session.Dispose(); this.session = null; } if (this.instance != null) { this.instance.Dispose(); this.instance = null; } }
public DistinguishedNameResolver(IsamDatabase database, DirectorySchema schema) { // Initialize the DN cache, while pre-caching the root DN as a sentinel. this.dnCache = new Dictionary <int, DistinguishedName>(); this.dnCache.Add(ADConstants.RootDNTag, new DistinguishedName()); // Cache AD schema and datatable cursor this.schema = schema; this.cursor = database.OpenCursor(ADConstants.DataTableName); }
// TODO: Internal? // TODO: ISchema public DirectorySchema(IsamDatabase database) { TableDefinition dataTable = database.Tables[ADConstants.DataTableName]; this.LoadColumnList(dataTable.Columns); this.LoadAttributeIndices(dataTable.Indices2); using (var cursor = database.OpenCursor(ADConstants.DataTableName)) { this.LoadClassList(cursor); this.LoadAttributeProperties(cursor); this.LoadPrefixMap(cursor); } // TODO: Load Ext-Int Map from hiddentable }
public LinkResolver(IsamDatabase database, DirectorySchema schema) { this.schema = schema; this.cursor = database.OpenCursor(ADConstants.LinkTableName); if (this.cursor.TableDefinition.Indices.Contains(linkIndex2008)) { this.cursor.SetCurrentIndex(linkIndex2008); } else { // Fallback to the old index if the newer one does not exist cursor.SetCurrentIndex(linkIndex2003); } // TODO: Load column ids instead of names }
public DistinguishedNameResolver(IsamDatabase database, DirectorySchema schema) { this.dnCache = new Dictionary <int, string>(); this.schema = schema; this.cursor = database.OpenCursor(ADConstants.DataTableName); }
/// <summary> /// Creates a new Active Directory database context. /// </summary> /// <param name="dbPath">dbFilePath must point to the DIT file on the local computer.</param> /// <param name="logPath">The path should point to a writeable folder on the local computer, where ESE log files will be created. If not specified, then temp folder will be used.</param> public DirectoryContext(string dbFilePath, bool readOnly, string logDirectoryPath = null) { if (!File.Exists(dbFilePath)) { // TODO: Extract as resource throw new FileNotFoundException("The specified database file does not exist.", dbFilePath); } this.DSADatabaseFile = dbFilePath; ValidateDatabaseState(this.DSADatabaseFile); this.DSAWorkingDirectory = Path.GetDirectoryName(this.DSADatabaseFile); string checkpointDirectoryPath = this.DSAWorkingDirectory; string tempDirectoryPath = this.DSAWorkingDirectory; this.DatabaseLogFilesPath = logDirectoryPath; if (this.DatabaseLogFilesPath != null) { if (!Directory.Exists(this.DatabaseLogFilesPath)) { // TODO: Extract as resource throw new FileNotFoundException("The specified log directory does not exist.", this.DatabaseLogFilesPath); } } else { this.DatabaseLogFilesPath = this.DSAWorkingDirectory; } // TODO: Exception handling? // HACK: IsamInstance constructor throws AccessDenied Exception when the path does not end with a backslash. this.instance = new IsamInstance(AddPathSeparator(checkpointDirectoryPath), AddPathSeparator(this.DatabaseLogFilesPath), AddPathSeparator(tempDirectoryPath), ADConstants.EseBaseName, JetInstanceName, readOnly, ADConstants.PageSize); try { var isamParameters = this.instance.IsamSystemParameters; // Set the size of the transaction log files to AD defaults. isamParameters.LogFileSize = ADConstants.EseLogFileSize; // Delete the log files that are not matching (generation wise) during soft recovery. isamParameters.DeleteOutOfRangeLogs = true; // Check the database for indexes over Unicode key columns that were built using an older version of the NLS library. isamParameters.EnableIndexChecking = true; // Automatically clean up indexes over Unicode key columns as necessary to avoid database format changes caused by changes to the NLS library. isamParameters.EnableIndexCleanup = true; // Retain only transaction log files that are younger than the current checkpoint. isamParameters.CircularLog = true; // Disable all database engine callbacks to application provided functions. This enables us to open Win2016 DBs on non-DC systems. isamParameters.DisableCallbacks = true; // TODO: Configure additional ISAM parameters // this.instance.IsamSystemParameters.EnableOnlineDefrag = false; this.session = this.instance.CreateSession(); this.session.AttachDatabase(this.DSADatabaseFile); this.isDBAttached = true; this.database = this.session.OpenDatabase(this.DSADatabaseFile); this.Schema = new DirectorySchema(this.database); this.SecurityDescriptorRersolver = new SecurityDescriptorRersolver(this.database); this.DistinguishedNameResolver = new DistinguishedNameResolver(this.database, this.Schema); this.LinkResolver = new LinkResolver(this.database, this.Schema); this.DomainController = new DomainController(this); } catch (EsentUnicodeTranslationFailException unicodeException) { // This typically happens while opening a Windows Server 2003 DIT on a newer system. this.Dispose(); throw new InvalidDatabaseStateException("There was a problem reading the database, which probably comes from a legacy system. Try defragmenting it first by running the 'esentutl /d ntds.dit' command.", this.DSADatabaseFile, unicodeException); } catch { // Free resources if anything failed this.Dispose(); throw; } }
/// <summary> /// Creates a new Active Directory database context. /// </summary> /// <param name="dbPath">dbFilePath must point to the DIT file on the local computer.</param> /// <param name="logPath">The path should point to a writeable folder on the local computer, where ESE log files will be created. If not specified, then temp folder will be used.</param> public DirectoryContext(string dbFilePath, bool readOnly, string logDirectoryPath = null) { if (!File.Exists(dbFilePath)) { throw new FileNotFoundException("The specified database file does not exist.", dbFilePath); } this.DSADatabaseFile = dbFilePath; ValidateDatabaseState(this.DSADatabaseFile); this.DSAWorkingDirectory = Path.GetDirectoryName(this.DSADatabaseFile); string checkpointDirectoryPath = this.DSAWorkingDirectory; string tempDatabasePath = Path.Combine(this.DSAWorkingDirectory, ADConstants.EseTempDatabaseName); this.DatabaseLogFilesPath = logDirectoryPath; if (this.DatabaseLogFilesPath != null) { if (!Directory.Exists(this.DatabaseLogFilesPath)) { throw new FileNotFoundException("The specified log directory does not exist.", this.DatabaseLogFilesPath); } } else { // Use the default location if an alternate log directory is not provided. this.DatabaseLogFilesPath = this.DSAWorkingDirectory; } // Note: IsamInstance constructor throws AccessDenied Exception when the path does not end with a backslash. this.instance = new IsamInstance(AddPathSeparator(checkpointDirectoryPath), AddPathSeparator(this.DatabaseLogFilesPath), tempDatabasePath, ADConstants.EseBaseName, JetInstanceName, readOnly, ADConstants.PageSize); try { var isamParameters = this.instance.IsamSystemParameters; // Set the size of the transaction log files to AD defaults. isamParameters.LogFileSize = ADConstants.EseLogFileSize; // Delete the log files that are not matching (generation wise) during soft recovery. isamParameters.DeleteOutOfRangeLogs = true; // Check the database for indexes over Unicode key columns that were built using an older version of the NLS library. isamParameters.EnableIndexChecking2 = true; // Automatically clean up indexes over Unicode key columns as necessary to avoid database format changes caused by changes to the NLS library. isamParameters.EnableIndexCleanup = true; // Retain only transaction log files that are younger than the current checkpoint. isamParameters.CircularLog = true; // Disable all database engine callbacks to application provided functions. This enables us to open Win2016 DBs on non-DC systems. isamParameters.DisableCallbacks = true; // Increase the limit of maximum open tables. isamParameters.MaxOpenTables = ADConstants.EseMaxOpenTables; // Enable backwards compatibility with the file naming conventions of earlier releases of the database engine. isamParameters.LegacyFileNames = ADConstants.EseLegacyFileNames; // Set EN-US to be used by any index over a Unicode key column. isamParameters.UnicodeIndexDefault = new JET_UNICODEINDEX() { lcid = ADConstants.EseIndexDefaultLocale, dwMapFlags = ADConstants.EseIndexDefaultCompareOptions }; // Force crash recovery to look for the database referenced in the transaction log in the specified folder. isamParameters.AlternateDatabaseRecoveryPath = this.DSAWorkingDirectory; if (!readOnly) { // Delete obsolete log files. isamParameters.DeleteOldLogs = true; if (EsentVersion.SupportsWindows10Features) { try { // Required for Windows Server 2022 compatibility, as it limits the transaction log file format to 8920. // Note: Usage of JET_efvUsePersistedFormat still causes minor DB format upgrade. // isamParameters.EngineFormatVersion = 0x40000002; // JET_efvUsePersistedFormat: Instructs the engine to use the minimal Engine Format Version of all loaded log and DB files. } catch (EsentInvalidParameterException) { // JET_efvUsePersistedFormat should be supported since Windows Server 2016. // Just continue even if it is not supported on the current Windows build. } } } this.session = this.instance.CreateSession(); this.session.AttachDatabase(this.DSADatabaseFile); this.isDBAttached = true; this.database = this.session.OpenDatabase(this.DSADatabaseFile); this.Schema = new DirectorySchema(this.database); this.SecurityDescriptorRersolver = new SecurityDescriptorRersolver(this.database); this.DistinguishedNameResolver = new DistinguishedNameResolver(this.database, this.Schema); this.LinkResolver = new LinkResolver(this.database, this.Schema); this.DomainController = new DomainController(this); } catch (EsentFileAccessDeniedException e) { // This exception was probably thrown by the OpenDatabase method // HACK: Do not dispose the IsamSession object. Its Dispose method would throw an exception, which is a bug in ISAM. GC.SuppressFinalize(this.session); this.session = null; // Free resources if anything failed this.Dispose(); throw; } catch (EsentErrorException e) { // Free resources if anything failed this.Dispose(); // EsentUnicodeTranslationFailException - This typically happens while opening a Windows Server 2003 DIT on a newer system. // EsentSecondaryIndexCorruptedException - This typically happens when opening a Windows Server 2012 R2 DIT on Windows 7. throw new InvalidDatabaseStateException("There was a problem reading the database, which probably comes from a different OS. Try defragmenting it first by running the 'esentutl /d ntds.dit' command.", this.DSADatabaseFile, e); } catch { // Free resources if anything failed this.Dispose(); throw; } }
public void Setup() { this.directory = SetupHelper.CreateRandomDirectory(); this.databaseName = Path.Combine(this.directory, "database.edb"); this.tableName = "table"; this.instance = SetupHelper.CreateNewInstance(this.directory); IsamSystemParameters isamSystemParameters = this.instance.IsamSystemParameters; isamSystemParameters.Recovery = "off"; this.sesid = this.instance.CreateSession(); this.sesid.CreateDatabase(this.databaseName); this.sesid.AttachDatabase(this.databaseName); this.dbid = this.sesid.OpenDatabase(this.databaseName); using (IsamTransaction transaction = new IsamTransaction(this.sesid)) { var tabledef = new TableDefinition(this.tableName); this.testColumnid = new ColumnDefinition(TestColumnName) { Type = typeof(string), }; tabledef.Columns.Add(this.testColumnid); tabledef.Columns.Add(new ColumnDefinition("TestColumn1") { Type = typeof(int), }); tabledef.Columns.Add(new ColumnDefinition("TestColumn2") { Type = typeof(int), }); var primaryIndex = new IndexDefinition("PrimaryIndex") { KeyColumns = { new KeyColumn( "TestColumn1", true), }, }; primaryIndex.Flags = IndexFlags.Primary; tabledef.Indices.Add(primaryIndex); var textIndex = new IndexDefinition("TextIndex") { KeyColumns = { new KeyColumn( "StringTestColumn", true), }, CultureInfo = new CultureInfo("en-CA"), }; tabledef.Indices.Add(textIndex); this.dbid.CreateTable(tabledef); transaction.Commit(); } this.tableid = this.dbid.OpenCursor(this.tableName); }
public SecurityDescriptorRersolver(IsamDatabase database) { this.cursor = database.OpenCursor(ADConstants.SecurityDescriptorTableName); }