Beispiel #1
0
        /// <summary>
        /// Creates a state with enabled journal.
        /// </summary>
        public static JournalState CreateEnabledJournal(VolumeMap volumeMap, IChangeJournalAccessor journal)
        {
            Contract.Requires(volumeMap != null);
            Contract.Requires(journal != null);

            return(new JournalState(volumeMap, journal));
        }
Beispiel #2
0
        /// <summary>
        /// Deserializes into an instance of <see cref="VolumeMap"/>.
        /// </summary>
        public static VolumeMap Deserialize(BuildXLReader reader)
        {
            Contract.Requires(reader != null);

            var volumeMap = new VolumeMap();
            int count     = reader.ReadInt32Compact();

            for (int i = 0; i < count; ++i)
            {
                ulong          serialNumber = reader.ReadUInt64();
                bool           isValid      = reader.ReadBoolean();
                VolumeGuidPath path         = isValid ? VolumeGuidPath.Create(reader.ReadString()) : VolumeGuidPath.Invalid;
                volumeMap.m_volumePathsBySerial.Add(serialNumber, path);
            }

            int numJunctionRoots = reader.ReadInt32Compact();

            for (int i = 0; i < numJunctionRoots; ++i)
            {
                string path = reader.ReadString();
                var    id   = FileIdAndVolumeId.Deserialize(reader);
                volumeMap.m_junctionRootFileIds.Add(path, id);
            }

            return(volumeMap);
        }
Beispiel #3
0
        /// <summary>
        /// Validate junction roots
        /// </summary>
        public void ValidateJunctionRoots(LoggingContext loggingContext, VolumeMap previousMap)
        {
            var changed   = new List <string>();
            var unchanged = new List <string>();

            foreach (var junction in m_junctionRootFileIds)
            {
                string                       path = junction.Key;
                FileIdAndVolumeId            previousId;
                JunctionRootValidationResult result;
                if (previousMap.m_junctionRootFileIds.TryGetValue(junction.Key, out previousId))
                {
                    if (previousId == junction.Value)
                    {
                        unchanged.Add(path);
                        result = JunctionRootValidationResult.Unchanged;
                    }
                    else
                    {
                        changed.Add(path);
                        result = JunctionRootValidationResult.Changed;
                    }
                }
                else
                {
                    changed.Add(path);
                    result = JunctionRootValidationResult.NotFound;
                }

                Logger.Log.ValidateJunctionRoot(loggingContext, path, result.ToString());
            }

            ChangedJunctionRoots   = changed;
            UnchangedJunctionRoots = unchanged;
        }
Beispiel #4
0
        /// <summary>
        /// Creates a map of local volumes. In the event of a collision which prevents constructing a serial -> path mapping,
        /// a warning is logged and those volumes are excluded from the map. On failure, returns null.
        /// </summary>
        public static VolumeMap CreateMapOfAllLocalVolumes(
            LoggingContext loggingContext,
            IReadOnlyList <string> junctionRoots       = null,
            IReadOnlyList <string> gvfsProjectionFiles = null)
        {
            var map = new VolumeMap();

            var guidPaths = new HashSet <VolumeGuidPath>();
            List <Tuple <VolumeGuidPath, ulong> > volumes = FileUtilities.ListVolumeGuidPathsAndSerials();

            foreach (var volume in volumes)
            {
                bool guidPathUnique = guidPaths.Add(volume.Item1);
                Contract.Assume(guidPathUnique, "Duplicate guid path");

                VolumeGuidPath collidedGuidPath;
                if (map.m_volumePathsBySerial.TryGetValue(volume.Item2, out collidedGuidPath))
                {
                    if (collidedGuidPath.IsValid)
                    {
                        // This could be an error. Instead, we optimistically create a partial map and hope that theese volumes are not relevant to the build.
                        // Some users have reported VHD-creation automation (concurrent with BuildXL) causing a collision.
                        Logger.Log.StorageVolumeCollision(loggingContext, volume.Item2, volume.Item1.Path, collidedGuidPath.Path);

                        // Poison this entry so that we know it is unusable on lookup (ambiguous)
                        map.m_volumePathsBySerial[volume.Item2] = VolumeGuidPath.Invalid;
                    }
                }
                else
                {
                    map.m_volumePathsBySerial.Add(volume.Item2, volume.Item1);
                }
            }

            toDictionary(junctionRoots, map.m_junctionRootFileIds);
            toDictionary(gvfsProjectionFiles, map.m_gvfsProjections);

            return(map);

            void toDictionary(IEnumerable <string> paths, Dictionary <string, FileIdAndVolumeId> result)
            {
                if (paths != null)
                {
                    foreach (var pathStr in paths)
                    {
                        FileIdAndVolumeId?id = TryGetFinalFileIdAndVolumeId(pathStr);
                        if (id.HasValue)
                        {
                            result[pathStr] = id.Value;
                        }
                    }
                }
            }
        }
Beispiel #5
0
        /// <summary>
        /// Tries to get <see cref="IChangeJournalAccessor"/>.
        /// </summary>
        public static Possible <IChangeJournalAccessor> TryGetJournalAccessor(VolumeMap volumeMap, string path)
        {
            Contract.Requires(volumeMap != null);
            Contract.Requires(!string.IsNullOrWhiteSpace(path));

            var inprocAccessor = new InProcChangeJournalAccessor();

            try
            {
                // When the current process runs with elevation, the process can access the journal directly. However, even with such a capability,
                // the process may fail in opening the volume handle. Thus, we need to verify that the process is able to open the volume handle.
                // If the operating system is Win10-RedStone2, it can directly access the journal as well.
                using (var file = FileUtilities.CreateFileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete))
                {
                    var volume = volumeMap.TryGetVolumePathForHandle(file.SafeFileHandle);
                    if (volume.IsValid)
                    {
                        // Journal accessor needs to know whether the OS allows unprivileged journal operations
                        // because some operation names (e.g., reading journal, getting volume handle) are different.
                        inprocAccessor.IsJournalUnprivileged = !CurrentProcess.IsElevated;

                        // Attempt to access journal. Any error means that the journal operations are not unprivileged yet in the host computer.
                        var result = inprocAccessor.QueryJournal(new QueryJournalRequest(volume));

                        if (!result.IsError)
                        {
                            if (result.Response.Succeeded)
                            {
                                return(inprocAccessor);
                            }
                            else
                            {
                                return(new Failure <string>(I($"Querying journal results in {result.Response.Status.ToString()}")));
                            }
                        }
                        else
                        {
                            return(new Failure <string>(result.Error.Message));
                        }
                    }
                    else
                    {
                        return(new Failure <string>(I($"Failed to get volume path for '{path}'")));
                    }
                }
            }
            catch (BuildXLException ex)
            {
                return(new Failure <string>(ex.Message));
            }
        }
Beispiel #6
0
        /// <summary>
        /// Deserializes into an instance of <see cref="VolumeMap"/>.
        /// </summary>
        public static VolumeMap Deserialize(BuildXLReader reader)
        {
            Contract.Requires(reader != null);

            var volumeMap = new VolumeMap();
            int count     = reader.ReadInt32Compact();

            for (int i = 0; i < count; ++i)
            {
                ulong          serialNumber = reader.ReadUInt64();
                bool           isValid      = reader.ReadBoolean();
                VolumeGuidPath path         = isValid ? VolumeGuidPath.Create(reader.ReadString()) : VolumeGuidPath.Invalid;
                volumeMap.m_volumePathsBySerial.Add(serialNumber, path);
            }

            ReadDictionary(reader, volumeMap.m_junctionRootFileIds);
            ReadDictionary(reader, volumeMap.m_gvfsProjections);

            return(volumeMap);
        }
Beispiel #7
0
        /// <summary>
        /// Validate junction roots
        /// </summary>
        public void ValidateJunctionRoots(LoggingContext loggingContext, VolumeMap previousMap)
        {
            m_unchangedJunctionRoots.Clear();
            m_changedGvfsProjections.Clear();

            foreach (var junction in m_junctionRootFileIds)
            {
                string path   = junction.Key;
                var    result = compareToPrevious(path, junction.Value, previousMap.m_junctionRootFileIds);
                Logger.Log.ValidateJunctionRoot(loggingContext, path, result.ToString());
                if (result == PathValidationResult.Unchanged)
                {
                    m_unchangedJunctionRoots.Add(path);
                }
            }

            foreach (var gvfsProjection in m_gvfsProjections)
            {
                string gvfsProjectionFile = gvfsProjection.Key;
                var    result             = compareToPrevious(gvfsProjectionFile, gvfsProjection.Value, previousMap.m_gvfsProjections);
                Logger.Log.ValidateJunctionRoot(loggingContext, gvfsProjectionFile, result.ToString());
                if (result != PathValidationResult.Unchanged)
                {
                    m_changedGvfsProjections.Add(gvfsProjectionFile);
                }
            }

            PathValidationResult compareToPrevious(string path, FileIdAndVolumeId id, Dictionary <string, FileIdAndVolumeId> previousMap)
            {
                var previousFound = previousMap.TryGetValue(path, out var previousId);

                return
                    (previousFound && previousId == id ? PathValidationResult.Unchanged :
                     previousFound && previousId != id ? PathValidationResult.Changed :
                     PathValidationResult.NewlyCreated);
            }
        }
Beispiel #8
0
 private JournalState(VolumeMap volumeMap, IChangeJournalAccessor journal)
 {
     Journal   = journal;
     VolumeMap = volumeMap;
 }
Beispiel #9
0
        /// <summary>
        /// Tries to get <see cref="IChangeJournalAccessor"/>.
        /// </summary>
        public static Optional <IChangeJournalAccessor> TryGetJournalAccessor(LoggingContext loggingContext, VolumeMap volumeMap, string path)
        {
            Contract.Requires(loggingContext != null);
            Contract.Requires(volumeMap != null);
            Contract.Requires(!string.IsNullOrWhiteSpace(path));

            var inprocAccessor = new InProcChangeJournalAccessor();

            // If the current process is called with elevation, it can directly access the journal.
            if (CurrentProcess.IsElevated)
            {
                return(inprocAccessor);
            }

            try
            {
                // If the operating system is Win10-RedStone2, it can directly access the journal as well.
                using (var file = FileUtilities.CreateFileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete))
                {
                    var volume = volumeMap.TryGetVolumePathForHandle(file.SafeFileHandle);
                    if (volume.IsValid)
                    {
                        // Journal accessor needs to know whether the OS allows unprivileged journal operations
                        // because some operation names (e.g., reading journal, getting volume handle) are different.
                        inprocAccessor.IsJournalUnprivileged = true;

                        // Attempt to access journal. Any error means that the journal operations are not unprivileged yet in the host computer.
                        var result = inprocAccessor.QueryJournal(new QueryJournalRequest(volume));
                        if (!result.IsError && result.Response.Succeeded)
                        {
                            return(inprocAccessor);
                        }
                    }
                }
            }
            catch (BuildXLException ex)
            {
                Logger.Log.FailedCheckingDirectJournalAccess(loggingContext, ex.Message);
                return(default(Optional <IChangeJournalAccessor>));
            }

            return(default(Optional <IChangeJournalAccessor>));
        }
Beispiel #10
0
 internal VolumeAccessor(VolumeMap map)
 {
     Contract.Requires(map != null);
     m_map    = map;
     Disposed = false;
 }