/// <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)); }
/// <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); }
/// <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; }
/// <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; } } } } }
/// <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)); } }
/// <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); }
/// <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); } }
private JournalState(VolumeMap volumeMap, IChangeJournalAccessor journal) { Journal = journal; VolumeMap = volumeMap; }
/// <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>)); }
internal VolumeAccessor(VolumeMap map) { Contract.Requires(map != null); m_map = map; Disposed = false; }