private bool TryInitializeUpgrader(out string error) { if (GVFSPlatform.Instance.UnderConstruction.SupportsGVFSUpgrade) { error = null; if (this.upgrader == null) { JsonTracer jsonTracer = new JsonTracer(GVFSConstants.GVFSEtwProviderName, "UpgradeVerb"); string logFilePath = GVFSEnlistment.GetNewGVFSLogFileName( ProductUpgraderInfo.GetLogDirectoryPath(), GVFSConstants.LogFileTypes.UpgradeVerb); jsonTracer.AddLogFileEventListener(logFilePath, EventLevel.Informational, Keywords.Any); this.tracer = jsonTracer; this.prerunChecker = new InstallerPreRunChecker(this.tracer, this.Confirmed ? GVFSConstants.UpgradeVerbMessages.GVFSUpgradeConfirm : GVFSConstants.UpgradeVerbMessages.GVFSUpgrade); IProductUpgrader upgrader; if (ProductUpgraderFactory.TryCreateUpgrader(out upgrader, this.tracer, out error)) { this.upgrader = upgrader; } else { error = $"ERROR: {error}"; } } return(this.upgrader != null); } else { error = $"ERROR: {GVFSConstants.UpgradeVerbMessages.GVFSUpgrade} is not supported on this operating system."; return(false); } }
private void CreateAndConfigureUpgradeLogDirectory() { string upgradeLogsPath = ProductUpgraderInfo.GetLogDirectoryPath(); DirectorySecurity upgradeLogsSecurity = this.GetUpgradeLogsDirectorySecurity(upgradeLogsPath); Directory.CreateDirectory(upgradeLogsPath, upgradeLogsSecurity); try { // Call SetAccessControl in case the directory already existed // (in which case the above CreateDirectory was a no-op) Directory.SetAccessControl(upgradeLogsPath, upgradeLogsSecurity); } catch (UnauthorizedAccessException e) { // UnauthorizedAccessException can occur when the upgrade logs directory was // created by a non-elevated user running 'gvfs upgrade'. Only the owner // is allowed to modify the ACLs, and if the logs directory was created by // the user running 'gvfs upgrade' then the Adminstrators group is not the owner. EventMetadata metadata = new EventMetadata(); metadata.Add("Exception", e.ToString()); metadata.Add( TracingConstants.MessageKey.InfoMessage, $"{nameof(this.CreateAndConfigureUpgradeLogDirectory)}: UnauthorizedAccessException when setting log directory ACLs"); this.tracer.RelatedEvent(EventLevel.Informational, "LogDirACL_UnauthorizedAccessException", metadata); // To avoid the ownership issues, rename the old log directory, create a new one, and migrate over // all of the contents of the old directory. this.MigrateUpgradeLogsToDirectoryWithFreshACLs(); } }
public override bool TryPrepareLogDirectory(out string error) { // Under normal circumstances // ProductUpgraderInfo.GetLogDirectoryPath will have // already been created by Scalar.Service. If for some // reason it does not (e.g. the service failed to start), // we need to create // ProductUpgraderInfo.GetLogDirectoryPath() explicity to // ensure that it has the correct ACLs (so that both admin // and non-admin users can create log files). If the logs // directory does not already exist, this call could fail // when running as a non-elevated user. string createDirectoryError; if (!this.FileSystem.TryCreateDirectoryWithAdminAndUserModifyPermissions(ProductUpgraderInfo.GetLogDirectoryPath(), out createDirectoryError)) { error = $"ERROR: Unable to create directory `{ProductUpgraderInfo.GetLogDirectoryPath()}`"; error += $"\n{createDirectoryError}"; error += $"\n\nTry running {ScalarConstants.UpgradeVerbMessages.ScalarUpgrade} from an elevated command prompt."; return(false); } error = null; return(true); }
/// <summary> /// Saves a copy of installer log from platform native installer in the /// upgrader diagnose logs directory. On the Mac, it is not possible to /// specify custom log file path as command line arg to the installer. /// </summary> private void SaveSystemInstallerLogs() { if (GVFSPlatform.Instance.SupportsSystemInstallLog) { string systemInstallerLog = GVFSPlatform.Instance.GetSystemInstallerLogPath(); if (!string.IsNullOrEmpty(systemInstallerLog)) { string destinationPath = GVFSEnlistment.GetNewGVFSLogFileName( ProductUpgraderInfo.GetLogDirectoryPath(), GVFSConstants.LogFileTypes.UpgradeSystemInstaller, this.UpgradeInstanceId); try { using (Stream sourceStream = this.fileSystem.OpenFileStream( systemInstallerLog, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, callFlushFileBuffers: false)) using (Stream destStream = this.fileSystem.OpenFileStream( destinationPath, FileMode.Create, FileAccess.Write, FileShare.None, callFlushFileBuffers: false)) { // Copy the last 100K from the system wide installer log. // System wide installer log (/var/log/install.log) holds // all messages from installd, including the ones that // it generated while installing VFSForGit. We don't // need to capture the whole file, which can be lengthy. // From my testing, the last 100K captured immediately // after upgrade, is found to be lengthy enough to // contain all of Git + GCM + Service + VFSForGit // installer log messages. long hundredKB = 100 * 1024; long copyFromOffset = sourceStream.Length > hundredKB ? sourceStream.Length - hundredKB : 0; sourceStream.Seek(copyFromOffset, SeekOrigin.Begin); sourceStream.CopyTo(destStream); } } catch (Exception ex) { EventMetadata metadata = new EventMetadata(); metadata.Add("Exception", ex.ToString()); this.tracer.RelatedError( metadata, $"{nameof(this.SaveSystemInstallerLogs)} - Error saving native installer log file."); } } else { this.tracer.RelatedError($"{nameof(this.SaveSystemInstallerLogs)} - Error getting native installer log file path."); } } }
private bool TryInitializeUpgrader(out string error) { if (this.DryRun && this.Confirmed) { error = $"{DryRunOption} and {ConfirmOption} arguments are not compatible."; return(false); } if (GVFSPlatform.Instance.UnderConstruction.SupportsGVFSUpgrade) { error = null; if (this.upgrader == null) { this.productUpgraderPlatformStrategy = GVFSPlatform.Instance.CreateProductUpgraderPlatformInteractions(this.fileSystem, tracer: null); if (!this.productUpgraderPlatformStrategy.TryPrepareLogDirectory(out error)) { return(false); } JsonTracer jsonTracer = new JsonTracer(GVFSConstants.GVFSEtwProviderName, "UpgradeVerb"); string logFilePath = GVFSEnlistment.GetNewGVFSLogFileName( ProductUpgraderInfo.GetLogDirectoryPath(), GVFSConstants.LogFileTypes.UpgradeVerb); jsonTracer.AddLogFileEventListener(logFilePath, EventLevel.Informational, Keywords.Any); this.tracer = jsonTracer; this.prerunChecker = new InstallerPreRunChecker(this.tracer, this.Confirmed ? GVFSPlatform.Instance.Constants.UpgradeConfirmCommandMessage : GVFSConstants.UpgradeVerbMessages.GVFSUpgrade); string gitBinPath = GVFSPlatform.Instance.GitInstallation.GetInstalledGitBinPath(); if (string.IsNullOrEmpty(gitBinPath)) { error = $"nameof(this.TryInitializeUpgrader): Unable to locate git installation. Ensure git is installed and try again."; return(false); } ICredentialStore credentialStore = new GitProcess(gitBinPath, workingDirectoryRoot: null); ProductUpgrader upgrader; if (ProductUpgrader.TryCreateUpgrader(this.tracer, this.fileSystem, new LocalGVFSConfig(), credentialStore, this.DryRun, this.NoVerify, out upgrader, out error)) { this.upgrader = upgrader; } else { error = $"ERROR: {error}"; } } return(this.upgrader != null); } else { error = $"ERROR: {GVFSConstants.UpgradeVerbMessages.GVFSUpgrade} is not supported on this operating system."; return(false); } }
public override void Execute() { this.ValidatePathParameter(this.EnlistmentRootPathParameter); this.Output.WriteLine("Most recent log files:"); string errorMessage; string enlistmentRoot; if (!GVFSPlatform.Instance.TryGetGVFSEnlistmentRoot(this.EnlistmentRootPathParameter, out enlistmentRoot, out errorMessage)) { this.ReportErrorAndExit( "Error: '{0}' is not a valid GVFS enlistment", this.EnlistmentRootPathParameter); } string gvfsLogsRoot = Path.Combine( enlistmentRoot, GVFSPlatform.Instance.Constants.DotGVFSRoot, GVFSConstants.DotGVFS.LogName); if (this.LogType == null) { this.DisplayMostRecent(gvfsLogsRoot, GVFSConstants.LogFileTypes.Clone); // By using MountPrefix ("mount") DisplayMostRecent will display either mount_verb, mount_upgrade, or mount_process, whichever is more recent this.DisplayMostRecent(gvfsLogsRoot, GVFSConstants.LogFileTypes.MountPrefix); this.DisplayMostRecent(gvfsLogsRoot, GVFSConstants.LogFileTypes.Prefetch); this.DisplayMostRecent(gvfsLogsRoot, GVFSConstants.LogFileTypes.Dehydrate); this.DisplayMostRecent(gvfsLogsRoot, GVFSConstants.LogFileTypes.Repair); this.DisplayMostRecent(gvfsLogsRoot, GVFSConstants.LogFileTypes.Sparse); string serviceLogsRoot = Path.Combine( GVFSPlatform.Instance.GetDataRootForGVFSComponent(GVFSConstants.Service.ServiceName), GVFSConstants.Service.LogDirectory); this.DisplayMostRecent(serviceLogsRoot, GVFSConstants.LogFileTypes.Service); this.DisplayMostRecent(ProductUpgraderInfo.GetLogDirectoryPath(), GVFSConstants.LogFileTypes.UpgradePrefix); } else { string logFile = FindNewestFileInFolder(gvfsLogsRoot, this.LogType); if (logFile == null) { this.ReportErrorAndExit("No log file found"); } else { foreach (string line in File.ReadAllLines(logFile)) { this.Output.WriteLine(line); } } } }
private void MigrateUpgradeLogsToDirectoryWithFreshACLs() { string upgradeLogsPath = ProductUpgraderInfo.GetLogDirectoryPath(); string tempUpgradeLogsPath = Path.Combine( Path.GetDirectoryName(upgradeLogsPath), ProductUpgraderInfo.LogDirectory + "_" + Guid.NewGuid().ToString("N")); this.tracer.RelatedInfo($"{nameof(this.MigrateUpgradeLogsToDirectoryWithFreshACLs)}: Renaming '{upgradeLogsPath}' to '{tempUpgradeLogsPath}'"); Directory.Move(upgradeLogsPath, tempUpgradeLogsPath); this.tracer.RelatedInfo($"{nameof(this.MigrateUpgradeLogsToDirectoryWithFreshACLs)}: Creating new '{upgradeLogsPath}' directory with appropriate ACLs"); DirectorySecurity upgradeLogsSecurity = this.GetUpgradeLogsDirectorySecurity(upgradeLogsPath); Directory.CreateDirectory(upgradeLogsPath, upgradeLogsSecurity); try { DirectoryInfo tempDirectoryInfo = new DirectoryInfo(tempUpgradeLogsPath); this.tracer.RelatedInfo($"Moving directories from '{tempUpgradeLogsPath}' to '{upgradeLogsPath}'"); foreach (DirectoryInfo logDirectoryInfo in tempDirectoryInfo.EnumerateDirectories(searchPattern: "*", searchOption: SearchOption.TopDirectoryOnly)) { Directory.Move(logDirectoryInfo.FullName, Path.Combine(upgradeLogsPath, logDirectoryInfo.Name)); } this.tracer.RelatedInfo($"Moving files from '{tempUpgradeLogsPath}' to '{upgradeLogsPath}'"); foreach (FileInfo logFileInfo in tempDirectoryInfo.EnumerateFiles(searchPattern: "*", searchOption: SearchOption.TopDirectoryOnly)) { File.Move(logFileInfo.FullName, Path.Combine(upgradeLogsPath, logFileInfo.Name)); } FileSystemInfo[] remainingChildren = tempDirectoryInfo.GetFileSystemInfos(); if (remainingChildren.Length > 0) { this.tracer.RelatedWarning( $"{nameof(this.MigrateUpgradeLogsToDirectoryWithFreshACLs)}: Skipping delete of old directory, {remainingChildren.Length} items still present on disk"); } else { PhysicalFileSystem fileSystem = new PhysicalFileSystem(); fileSystem.DeleteDirectory(tempUpgradeLogsPath, recursive: false); } } catch (Exception e) { EventMetadata metadata = new EventMetadata(); metadata.Add("Exception", e.ToString()); metadata.Add(nameof(tempUpgradeLogsPath), tempUpgradeLogsPath); metadata.Add(nameof(upgradeLogsPath), upgradeLogsPath); this.tracer.RelatedWarning( metadata, $"{nameof(this.MigrateUpgradeLogsToDirectoryWithFreshACLs)}: Caught exception migrating files from the old upgrade log directory"); } }
private bool TryInitializeUpgrader(out string error) { if (this.DryRun && this.Confirmed) { error = $"{DryRunOption} and {ConfirmOption} arguments are not compatible."; return(false); } if (GVFSPlatform.Instance.UnderConstruction.SupportsGVFSUpgrade) { error = null; if (this.upgrader == null) { // Under normal circumstances ProductUpgraderInfo.GetLogDirectoryPath will have already been created by GVFS.Service. If for some reason it // does not (e.g. the service failed to start), we need to create ProductUpgraderInfo.GetLogDirectoryPath() explicity to ensure that // it has the correct ACLs (so that both admin and non-admin users can create log files). // If the logs directory does not already exist, this call could fail when running as a non-elevated user. string createDirectoryError; if (!this.fileSystem.TryCreateDirectoryWithAdminAndUserModifyPermissions(ProductUpgraderInfo.GetLogDirectoryPath(), out createDirectoryError)) { error = $"ERROR: Unable to create directory `{ProductUpgraderInfo.GetLogDirectoryPath()}`"; error += $"\n{createDirectoryError}"; error += $"\n\nTry running {GVFSConstants.UpgradeVerbMessages.GVFSUpgrade} from an elevated command prompt."; return(false); } JsonTracer jsonTracer = new JsonTracer(GVFSConstants.GVFSEtwProviderName, "UpgradeVerb"); string logFilePath = GVFSEnlistment.GetNewGVFSLogFileName( ProductUpgraderInfo.GetLogDirectoryPath(), GVFSConstants.LogFileTypes.UpgradeVerb); jsonTracer.AddLogFileEventListener(logFilePath, EventLevel.Informational, Keywords.Any); this.tracer = jsonTracer; this.prerunChecker = new InstallerPreRunChecker(this.tracer, this.Confirmed ? GVFSConstants.UpgradeVerbMessages.GVFSUpgradeConfirm : GVFSConstants.UpgradeVerbMessages.GVFSUpgrade); ProductUpgrader upgrader; if (ProductUpgrader.TryCreateUpgrader(this.tracer, this.fileSystem, this.DryRun, this.NoVerify, out upgrader, out error)) { this.upgrader = upgrader; } else { error = $"ERROR: {error}"; } } return(this.upgrader != null); } else { error = $"ERROR: {GVFSConstants.UpgradeVerbMessages.GVFSUpgrade} is not supported on this operating system."; return(false); } }
private void CreateAndConfigureProgramDataDirectories() { string serviceDataRootPath = ScalarPlatform.Instance.GetSecureDataRootForScalar(); // Create Scalar.Service and Scalar.Upgrade related directories (if they don't already exist) // TODO #136: Determine if we still should be creating Scalar.Service here DirectoryEx.CreateDirectory(serviceDataRootPath); DirectoryEx.CreateDirectory(this.serviceDataLocation); DirectoryEx.CreateDirectory(ProductUpgraderInfo.GetUpgradeProtectedDataDirectory()); // Special rules for the upgrader logs and registry, as non-elevated users need to be be able to write this.CreateAndConfigureUserWriteableDirectory(this.repoRegistryLocation); this.CreateAndConfigureUserWriteableDirectory(ProductUpgraderInfo.GetLogDirectoryPath()); this.CreateAndConfigureUserWriteableDirectory(ScalarPlatform.Instance.GetLogsDirectoryForGVFSComponent(ScalarConstants.Service.UIName)); }
private void CreateAndConfigureProgramDataDirectories() { string serviceDataRootPath = Path.GetDirectoryName(this.serviceDataLocation); DirectorySecurity serviceDataRootSecurity = this.GetServiceDirectorySecurity(serviceDataRootPath); // Create GVFS.Service and GVFS.Upgrade related directories (if they don't already exist) Directory.CreateDirectory(serviceDataRootPath, serviceDataRootSecurity); Directory.CreateDirectory(this.serviceDataLocation, serviceDataRootSecurity); Directory.CreateDirectory(ProductUpgraderInfo.GetUpgradeProtectedDataDirectory(), serviceDataRootSecurity); // Ensure the ACLs are set correctly on any files or directories that were already created (e.g. after upgrading VFS4G) Directory.SetAccessControl(serviceDataRootPath, serviceDataRootSecurity); // Special rules for the upgrader logs, as non-elevated users need to be be able to write this.CreateAndConfigureLogDirectory(ProductUpgraderInfo.GetLogDirectoryPath()); this.CreateAndConfigureLogDirectory(GVFSPlatform.Instance.GetDataRootForGVFSComponent(GVFSConstants.Service.UIName)); }
private void CreateAndConfigureUpgradeLogDirectory() { string upgradeLogsPath = ProductUpgraderInfo.GetLogDirectoryPath(); string error; if (!GVFSPlatform.Instance.FileSystem.TryCreateDirectoryWithAdminAndUserModifyPermissions(upgradeLogsPath, out error)) { EventMetadata metadata = new EventMetadata(); metadata.Add("Area", EtwArea); metadata.Add(nameof(upgradeLogsPath), upgradeLogsPath); metadata.Add(nameof(error), error); this.tracer.RelatedWarning( metadata, $"{nameof(this.CreateAndConfigureUpgradeLogDirectory)}: Failed to create upgrade logs directory", Keywords.Telemetry); } }
public UpgradeOrchestrator() { string logFilePath = GVFSEnlistment.GetNewGVFSLogFileName( ProductUpgraderInfo.GetLogDirectoryPath(), GVFSConstants.LogFileTypes.UpgradeProcess); JsonTracer jsonTracer = new JsonTracer(GVFSConstants.GVFSEtwProviderName, "UpgradeProcess"); jsonTracer.AddLogFileEventListener( logFilePath, DefaultEventLevel, Keywords.Any); this.tracer = jsonTracer; this.preRunChecker = new InstallerPreRunChecker(this.tracer, GVFSConstants.UpgradeVerbMessages.GVFSUpgradeConfirm); this.output = Console.Out; this.input = Console.In; this.mount = false; this.ExitCode = ReturnCode.Success; }
public override bool TryRunInstaller(InstallActionWrapper installActionWrapper, out string error) { string localError = null; int installerExitCode; bool installSuccessful = true; using (ITracer activity = this.tracer.StartActivity(nameof(this.TryRunInstaller), EventLevel.Informational)) { InstallActionInfo currentInstallAction = null; try { string platformKey = InstallManifest.WindowsPlatformKey; if (!this.TryRecursivelyDeleteInstallerDirectory(out error)) { return(false); } if (!this.noVerify) { if (!this.nuGetFeed.VerifyPackage(this.DownloadedPackagePath)) { error = "Package signature validation failed. Check the upgrade logs for more details."; activity.RelatedError(error); this.fileSystem.DeleteFile(this.DownloadedPackagePath); return(false); } } this.UnzipPackage(); this.installManifest = InstallManifest.FromJsonFile(Path.Combine(this.ExtractedInstallerPath, ContentDirectoryName, InstallManifestFileName)); if (!this.installManifest.PlatformInstallManifests.TryGetValue(platformKey, out InstallManifestPlatform platformInstallManifest) || platformInstallManifest == null) { activity.RelatedError($"Extracted InstallManifest from JSON, but there was no entry for {platformKey}."); error = $"No entry in the manifest for the current platform ({platformKey}). Please verify the upgrade package."; return(false); } activity.RelatedInfo($"Extracted InstallManifest from JSON. InstallActions: {platformInstallManifest.InstallActions.Count}"); foreach (InstallActionInfo entry in platformInstallManifest.InstallActions) { currentInstallAction = entry; string installerPath = Path.Combine(this.ExtractedInstallerPath, ContentDirectoryName, entry.InstallerRelativePath); string args = entry.Args ?? string.Empty; // Replace tokens on args string processedArgs = NuGetUpgrader.ReplaceArgTokens(args, this.UpgradeInstanceId, ProductUpgraderInfo.GetLogDirectoryPath()); activity.RelatedInfo( "Running install action: Name: {0}, Version: {1}, InstallerPath: {2} RawArgs: {3}, ProcessedArgs: {4}", entry.Name, entry.Version, installerPath, args, processedArgs); string progressMessage = string.IsNullOrWhiteSpace(entry.Version) ? $"Running {entry.Name}" : $"Running {entry.Name} (version {entry.Version})"; installActionWrapper( () => { if (!this.dryRun) { this.RunInstaller(installerPath, processedArgs, out installerExitCode, out localError); } else { // We add a sleep here to ensure // the message for this install // action is written to the // console. Even though the // message is written with a delay // of 0, the messages are not // always written out. If / when // we can ensure that the message // is written out to console, then // we can remove this sleep. Thread.Sleep(1500); installerExitCode = 0; } installSuccessful = installerExitCode == 0; return(installSuccessful); }, progressMessage); if (!installSuccessful) { break; } } } catch (Exception ex) { localError = ex.Message; installSuccessful = false; } if (!installSuccessful) { string installActionName = string.IsNullOrEmpty(currentInstallAction?.Name) ? "installer" : currentInstallAction.Name; error = string.IsNullOrEmpty(localError) ? $"The {installActionName} failed, but no error message was provided by the failing command." : $"The {installActionName} failed with the following error: {localError}"; activity.RelatedError($"Could not complete all install actions. The following error was encountered: {error}"); return(false); } else { activity.RelatedInfo($"Install actions completed successfully."); error = null; return(true); } } }