XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet
公开
关注 0 Fork 0 Star 0
UTF-8
using XFEEliteDangerous.Client;
using System.Security.AccessControl;
using System.Security.Principal;
using System.Text.Json;

namespace XFEEliteDangerous.Bootstrapper;

/// <summary>Temporary read/execute only access for one newly created AppContainer SID.</summary>
internal sealed class DirectoryAccessLease : IDisposable
{
    private readonly string path,sid,originalSddl;
    private bool disposed;
    internal bool OriginalAclRestored { get; private set; }
    internal bool OriginalAclRulesRestored { get; private set; }
    internal DirectoryAccessLease(string directory,string appContainerSid,string backupPath)
    {
        using var update=AcquireUpdate();
        path=SafePaths.LocalAbsolute(directory); SafePaths.NoReparseAncestors(path);
        if(!string.Equals(path,@"E:\Elite Dangerous",StringComparison.OrdinalIgnoreCase)) throw new ArgumentException("This temporary game lease is restricted to the explicitly authorized E diagnostic copy.");
        if(!Directory.Exists(path)) throw new DirectoryNotFoundException();
        sid=appContainerSid;
        if(!sid.StartsWith("S-1-15-2-",StringComparison.Ordinal)||sid=="S-1-15-2-1"||sid.Split('-').Length<10) throw new ArgumentException("A specific new AppContainer SID is required.");
        var info=new DirectoryInfo(path); var security=info.GetAccessControl();
        if(security.GetAccessRules(true,false,typeof(SecurityIdentifier)).Cast<FileSystemAccessRule>().Any(x=>x.IdentityReference.Value==sid)) throw new IOException("The random AppContainer SID already has explicit directory access.");
        originalSddl=security.GetSecurityDescriptorSddlForm(AccessControlSections.Access);
        using(var backup=SafePaths.CreateNewReport(backupPath)) JsonSerializer.Serialize(backup,new{kind="temporary-appcontainer-rx-acl-lease",directory=path,appContainerSid=sid,originalAccessSddl=originalSddl,rights="ReadAndExecute",gameContentModified=false});
        security.AddAccessRule(new FileSystemAccessRule(new SecurityIdentifier(sid),FileSystemRights.ReadAndExecute,InheritanceFlags.ContainerInherit|InheritanceFlags.ObjectInherit,PropagationFlags.None,AccessControlType.Allow));
        info.SetAccessControl(security);
    }
    public void Dispose()
    {
        if(disposed) return; disposed=true;
        using var update=AcquireUpdate();
        SafePaths.NoReparseAncestors(path);
        var info=new DirectoryInfo(path); var current=info.GetAccessControl();
        foreach(var rule in current.GetAccessRules(true,false,typeof(SecurityIdentifier)).Cast<FileSystemAccessRule>().Where(x=>x.IdentityReference.Value==sid).ToArray()) current.RemoveAccessRuleSpecific(rule);
        // Never replace the descriptor wholesale: concurrent changes for other principals survive.
        info.SetAccessControl(current);
        var restored=info.GetAccessControl().GetSecurityDescriptorSddlForm(AccessControlSections.Access);
        OriginalAclRestored=restored==originalSddl;
        // NTFS can canonicalize SE_DACL_AUTO_INHERITED without changing a single ACE or protection bit.
        OriginalAclRulesRestored=restored.Replace("D:AI","D:",StringComparison.Ordinal)==originalSddl.Replace("D:AI","D:",StringComparison.Ordinal);
    }
    private sealed class AclUpdate : IDisposable
    {
        private readonly Mutex mutex=new(false,@"Local\XFEEliteDangerous.ECopyDirectoryAcl");
        internal AclUpdate(){try{if(!mutex.WaitOne(TimeSpan.FromSeconds(15)))throw new TimeoutException(AppText.Get("DirectoryAccessLease.Text001"));}catch(AbandonedMutexException){}}
        public void Dispose(){mutex.ReleaseMutex();mutex.Dispose();}
    }
    private static IDisposable AcquireUpdate()=>new AclUpdate();
}