276 lines
9.2 KiB
C#
276 lines
9.2 KiB
C#
namespace DSInternals.DataStore
|
|
{
|
|
using DSInternals.Common;
|
|
using DSInternals.Common.Data;
|
|
using Microsoft.Database.Isam;
|
|
using System;
|
|
using System.Linq;
|
|
using System.Security.AccessControl;
|
|
using System.Security.Principal;
|
|
|
|
public sealed class DatastoreObject : DirectoryObject
|
|
{
|
|
private DirectoryContext context;
|
|
private Cursor cursor;
|
|
public DatastoreObject(Cursor datatableCursor, DirectoryContext context)
|
|
{
|
|
this.cursor = datatableCursor;
|
|
this.context = context;
|
|
}
|
|
public override string DistinguishedName
|
|
{
|
|
get
|
|
{
|
|
var dn = this.context.DistinguishedNameResolver.Resolve(this.DNTag);
|
|
return dn.ToString();
|
|
}
|
|
}
|
|
|
|
public int DNTag
|
|
{
|
|
get
|
|
{
|
|
Columnid columnId = this.context.Schema.FindColumnId(CommonDirectoryAttributes.DNTag);
|
|
int? dnt = cursor.RetrieveColumnAsDNTag(columnId);
|
|
return dnt.Value;
|
|
}
|
|
}
|
|
|
|
public override Guid Guid
|
|
{
|
|
get
|
|
{
|
|
Guid? guid;
|
|
this.ReadAttribute(CommonDirectoryAttributes.ObjectGUID, out guid);
|
|
return guid ?? Guid.Empty;
|
|
}
|
|
}
|
|
|
|
public override SecurityIdentifier Sid
|
|
{
|
|
get
|
|
{
|
|
SecurityIdentifier sid;
|
|
this.ReadAttribute(CommonDirectoryAttributes.ObjectSid, out sid);
|
|
return sid;
|
|
}
|
|
}
|
|
|
|
protected override bool HasBigEndianRid
|
|
{
|
|
get
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
public bool AddAttribute(string name, SecurityIdentifier[] valuesToAdd)
|
|
{
|
|
Columnid columnId = this.context.Schema.FindColumnId(CommonDirectoryAttributes.SIDHistory);
|
|
bool hasChanged = this.cursor.AddMultiValue(columnId, valuesToAdd);
|
|
return hasChanged;
|
|
}
|
|
|
|
public void Delete()
|
|
{
|
|
// TODO: Check if we are in a transaction
|
|
using (var transaction = this.context.BeginTransaction())
|
|
{
|
|
this.cursor.Delete();
|
|
}
|
|
// Invalidate this object:
|
|
this.cursor = null;
|
|
}
|
|
|
|
public override bool HasAttribute(string name)
|
|
{
|
|
if(this.context.Schema.ContainsAttribute(name))
|
|
{
|
|
// Read the appropriate column and check if it has a value
|
|
Columnid columnId = this.context.Schema.FindColumnId(name);
|
|
long columnSize = cursor.Record.SizeOf(columnId);
|
|
return columnSize > 0;
|
|
}
|
|
else
|
|
{
|
|
// The schema does not even contain this attribute, so the object cannot have it.
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public override void ReadAttribute(string name, out byte[] value)
|
|
{
|
|
if(this.context.Schema.ContainsAttribute(name))
|
|
{
|
|
Columnid columnId = this.context.Schema.FindColumnId(name);
|
|
value = this.cursor.RetrieveColumnAsByteArray(columnId);
|
|
}
|
|
else
|
|
{
|
|
value = null;
|
|
}
|
|
}
|
|
|
|
public override void ReadAttribute(string name, out byte[][] value)
|
|
{
|
|
if (this.context.Schema.ContainsAttribute(name))
|
|
{
|
|
Columnid columnId = this.context.Schema.FindColumnId(name);
|
|
value = this.cursor.RetrieveColumnAsMultiByteArray(columnId);
|
|
}
|
|
else
|
|
{
|
|
value = null;
|
|
}
|
|
}
|
|
|
|
public override void ReadAttribute(string name, out int? value)
|
|
{
|
|
if (this.context.Schema.ContainsAttribute(name))
|
|
{
|
|
Columnid columnId = this.context.Schema.FindColumnId(name);
|
|
value = this.cursor.RetrieveColumnAsInt(columnId);
|
|
}
|
|
else
|
|
{
|
|
value = null;
|
|
}
|
|
}
|
|
|
|
public override void ReadAttribute(string name, out string value)
|
|
{
|
|
if (this.context.Schema.ContainsAttribute(name))
|
|
{
|
|
Columnid columnId = this.context.Schema.FindColumnId(name);
|
|
value = this.cursor.RetrieveColumnAsString(columnId);
|
|
}
|
|
else
|
|
{
|
|
value = null;
|
|
}
|
|
}
|
|
|
|
public override void ReadAttribute(string name, out string[] values)
|
|
{
|
|
if (this.context.Schema.ContainsAttribute(name))
|
|
{
|
|
Columnid columnId = this.context.Schema.FindColumnId(name);
|
|
values = this.cursor.RetrieveColumnAsStringArray(columnId);
|
|
}
|
|
else
|
|
{
|
|
values = null;
|
|
}
|
|
}
|
|
|
|
public override void ReadAttribute(string name, out long? value)
|
|
{
|
|
if (this.context.Schema.ContainsAttribute(name))
|
|
{
|
|
Columnid columnId = this.context.Schema.FindColumnId(name);
|
|
value = this.cursor.RetrieveColumnAsLong(columnId);
|
|
}
|
|
else
|
|
{
|
|
value = null;
|
|
}
|
|
}
|
|
|
|
public override void ReadAttribute(string name, out DistinguishedName value)
|
|
{
|
|
if (this.context.Schema.ContainsAttribute(name))
|
|
{
|
|
Columnid columnId = this.context.Schema.FindColumnId(name);
|
|
var dnt = this.cursor.RetrieveColumnAsDNTag(columnId);
|
|
value = this.context.DistinguishedNameResolver.Resolve(dnt.Value);
|
|
}
|
|
else
|
|
{
|
|
value = null;
|
|
}
|
|
}
|
|
|
|
public override void ReadAttribute(string name, out RawSecurityDescriptor value)
|
|
{
|
|
byte[] binarySecurityDescriptorId;
|
|
// The value is stored as 8 bytes instead of 64-bit integer
|
|
this.ReadAttribute(name, out binarySecurityDescriptorId);
|
|
|
|
if (binarySecurityDescriptorId == null)
|
|
{
|
|
value = null;
|
|
return;
|
|
}
|
|
long securityDescriptorId = BitConverter.ToInt64(binarySecurityDescriptorId, 0);
|
|
value = this.context.SecurityDescriptorRersolver.GetDescriptor(securityDescriptorId);
|
|
}
|
|
|
|
public void ReadAttribute(string name, out AttributeMetadataCollection value)
|
|
{
|
|
|
|
byte[] binaryValue;
|
|
this.ReadAttribute(name, out binaryValue);
|
|
value = new AttributeMetadataCollection(binaryValue);
|
|
}
|
|
|
|
public override void ReadLinkedValues(string attributeName, out byte[][] values)
|
|
{
|
|
var rawValues = this.context.LinkResolver.GetLinkedValues(this.DNTag, attributeName);
|
|
// Cut off the first 4 bytes, which is the length of the entire structure.
|
|
values = rawValues.Select( rawValue => rawValue.Cut(sizeof(int)) ).ToArray();
|
|
}
|
|
|
|
public bool SetAttribute<T>(string name, T? newValue) where T : struct
|
|
{
|
|
// TODO: This must be called from a transaction
|
|
Columnid columnId = this.context.Schema.FindColumnId(name);
|
|
bool hasChanged = this.cursor.SetValue(columnId, newValue);
|
|
return hasChanged;
|
|
}
|
|
|
|
public bool SetAttribute(string name, DateTime newValue)
|
|
{
|
|
|
|
return this.SetAttribute<long>(name, newValue.ToFileTime());
|
|
}
|
|
|
|
public bool SetAttribute(string name, byte[] newValue)
|
|
{
|
|
Columnid columnId = this.context.Schema.FindColumnId(name);
|
|
bool hasChanged = this.cursor.SetValue (columnId, newValue);
|
|
return hasChanged;
|
|
}
|
|
|
|
public void UpdateAttributeMeta(string attributeName, long usn, DateTime time)
|
|
{
|
|
Validator.AssertNotNull(attributeName, "attributeName");
|
|
this.UpdateAttributeMeta(new string[] { attributeName }, usn, time);
|
|
}
|
|
|
|
public void UpdateAttributeMeta(string[] attributeNames, long usn, DateTime time)
|
|
{
|
|
Validator.AssertNotNull(attributeNames, nameof(attributeNames));
|
|
|
|
ColumnAccessor record = this.cursor.EditRecord;
|
|
|
|
// Update the uSNChanged attribute
|
|
this.SetAttribute<long>(CommonDirectoryAttributes.USNChanged, usn);
|
|
|
|
// Update the whenChanged attribute
|
|
this.SetAttribute<long>(CommonDirectoryAttributes.WhenChanged, time.ToGeneralizedTime());
|
|
|
|
// Update the replPropertyMetaData attribute (read-modify-write)
|
|
this.ReadAttribute(CommonDirectoryAttributes.PropertyMetaData, out AttributeMetadataCollection meta);
|
|
|
|
foreach(var attributeName in attributeNames)
|
|
{
|
|
// We go through all attributes that are changed in this transaction
|
|
int attributeId = this.context.Schema.FindAttribute(attributeName).Id.Value;
|
|
meta.Update(attributeId, this.context.DomainController.InvocationId, time, usn);
|
|
}
|
|
|
|
this.SetAttribute(CommonDirectoryAttributes.PropertyMetaData, meta.ToByteArray());
|
|
}
|
|
}
|
|
}
|