/// <summary><see cref="ProcessUtilities.AttachContainerToJobObject(IntPtr, IReadOnlyDictionary{ExpandedAbsolutePath, IReadOnlyList{ExpandedAbsolutePath}}, out IEnumerable{string})"/></summary>
        public void AttachContainerToJobObject(IntPtr hJob, IReadOnlyDictionary <ExpandedAbsolutePath, IReadOnlyList <ExpandedAbsolutePath> > redirectedDirectories, out IEnumerable <string> warnings)
        {
            try
            {
                if (NativeContainerUtilities.WcCreateDescriptionFromXml(s_containerDescription, out var description) != 0)
                {
                    throw new NativeWin32Exception(Marshal.GetLastWin32Error(), I($"Unable to create a description for a container for job object {hJob}."));
                }

                if (NativeContainerUtilities.WcCreateContainer(hJob, description, isServerSilo: false) != 0)
                {
                    throw new NativeWin32Exception(Marshal.GetLastWin32Error(), I($"Unable to create a container for job object {hJob}."));
                }

                NativeContainerUtilities.WcDestroyDescription(description);

                var wciRetries = new List <string>();
                ConfigureContainer(hJob, redirectedDirectories, wciRetries);

                warnings = wciRetries;
            }
            catch (NativeWin32Exception ex)
            {
                throw new BuildXLException("Unable to create container.", ex);
            }
        }
        /// <summary><see cref="ProcessUtilities.TryCleanUpContainer"/></summary>
        public bool TryCleanUpContainer(IntPtr hJob, out IEnumerable <string> errors)
        {
            var cleanUpErrors = new List <string>();

            var result = NativeContainerUtilities.WcCleanupContainer(hJob, null);

            if (result != 0)
            {
                cleanUpErrors.Add(I($"Cannot clean up container for job object {hJob}. Details: {NativeWin32Exception.GetFormattedMessageForNativeErrorCode(result)}"));
            }

            errors = cleanUpErrors;
            return(cleanUpErrors.Count == 0);
        }
        private static int LoadFilterWithPrivilege(string filterName)
        {
            try
            {
                NativeContainerUtilities.RtlImpersonateSelf(NativeContainerUtilities.SecurityImpersonationLevel.SecurityImpersonation, 0, out _);
                NativeContainerUtilities.RtlAdjustPrivilege(NativeContainerUtilities.Priviledge.SE_LOAD_DRIVER_PRIVILEGE, true, true, out _);

                var result = NativeContainerUtilities.FilterLoad(filterName);
                return(result);
            }
            finally
            {
                NativeContainerUtilities.RevertToSelf();
            }
        }
        private static void ConfigureBindFilter(IntPtr hJob, IReadOnlyCollection <ExpandedAbsolutePath> sourcePaths, string targetPath)
        {
            foreach (ExpandedAbsolutePath sourcePath in sourcePaths)
            {
                var hresult = NativeContainerUtilities.BfSetupFilter(
                    hJob,
                    // This flag has to be passed when WCI and Bind are configured for the same silo
                    NativeContainerUtilities.BfSetupFilterFlags.BINDFLT_FLAG_USE_CURRENT_SILO_MAPPING,
                    sourcePath.ExpandedPath,
                    targetPath,
                    CollectionUtilities.EmptyArray <string>(),
                    0);

                if (hresult != 0)
                {
                    throw new NativeWin32Exception(Marshal.GetLastWin32Error(), I($"Unable to setup the Bind filter from '{sourcePath}' to '{targetPath}'."));
                }
            }
        }
        private static void ConfigureWciFilter(IntPtr hJob, IReadOnlyCollection <ExpandedAbsolutePath> sourcePaths, string destinationPath, List <string> wciRetries)
        {
            var layerDescriptors = new NativeContainerUtilities.WC_LAYER_DESCRIPTOR[sourcePaths.Count];

            int i = 0;

            foreach (ExpandedAbsolutePath sourcePath in sourcePaths)
            {
                var layerDescriptor = new NativeContainerUtilities.WC_LAYER_DESCRIPTOR
                {
                    // By default we always set the layer so it inherits the descriptor. This
                    // makes hardlinks (ACLed for deny-write) and exposed via copy-on-write writable
                    // The sparse flag is not set explicitly since we use the general sparse isolation mode
                    // which implies that each layer will be sparse
                    Flags   = NativeContainerUtilities.LayerDescriptorFlags.InheritSecurity,
                    LayerId = NativeContainerUtilities.ToGuid(Guid.NewGuid()),
                    Path    = sourcePath.ExpandedPath,
                };
                layerDescriptors[i] = layerDescriptor;
                i++;
            }

            var reparsePointData = new NativeContainerUtilities.WC_REPARSE_POINT_DATA
            {
                Flags      = 0,
                LayerId    = layerDescriptors[0].LayerId,
                NameLength = 0,
                Name       = string.Empty
            };

            var hresult = -1;

            hresult = NativeContainerUtilities.WciSetReparsePointData(
                destinationPath,
                ref reparsePointData,
                (UInt16)Marshal.OffsetOf(typeof(NativeContainerUtilities.WC_REPARSE_POINT_DATA), "Name"));

            if (hresult != 0)
            {
                throw new NativeWin32Exception(Marshal.GetLastWin32Error(), I($"Unable to setup the reparse point data for '{destinationPath}'."));
            }

            hresult = -1;
            // We try to setup the WCI filter on a retry loop to workaround an existing issue
            // in the driver behavior, where the setup sometimes fails.
            var retries = s_wciRetries;

            while (hresult != 0 && retries > 0)
            {
                // Isolation is set to hard because we want the virtualized source path to be
                // completely isolated (e.g. with 'hard' we will get copy-on-write behavior and
                // tombstones when any deletion happens inside the container).
                // It is also set as sparse so all layers recursively merge without needing an explicit reparse point
                // for each of them
                hresult = NativeContainerUtilities.WciSetupFilter(
                    hJob,
                    NativeContainerUtilities.WC_ISOLATION_MODE.IsolationModeSparseHard,
                    destinationPath,
                    layerDescriptors,
                    (uint)layerDescriptors.Length,
                    NativeContainerUtilities.WC_NESTING_MODE.WcNestingModeInner);

                retries--;
                if (hresult != 0)
                {
                    wciRetries.Add(I($"Error setting WCI filter for {hJob} from '{string.Join(Environment.NewLine, sourcePaths)}' to '{destinationPath}'. Retries left: {retries}. Details: {NativeWin32Exception.GetFormattedMessageForNativeErrorCode(hresult)}"));
                }
            }

            if (hresult != 0)
            {
                throw new NativeWin32Exception(Marshal.GetLastWin32Error(), I($"Unable to setup the WCI filter for source paths '{string.Join(Environment.NewLine, sourcePaths)}' to destination path '{destinationPath}'."));
            }
        }