From b7974d5249c6077d1b38a09386019bd6e903bcaf Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Fri, 16 Sep 2016 11:03:02 -0500 Subject: [PATCH 1/5] (GH-839) More switch names for dependency apply When specifying an apply of install arguments and package parameters, it would be best to have multiple ways to specify those options. Add shorter option names --- .../infrastructure.app/commands/ChocolateyInstallCommand.cs | 4 ++-- .../infrastructure.app/commands/ChocolateyUninstallCommand.cs | 4 ++-- .../infrastructure.app/commands/ChocolateyUpgradeCommand.cs | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/chocolatey/infrastructure.app/commands/ChocolateyInstallCommand.cs b/src/chocolatey/infrastructure.app/commands/ChocolateyInstallCommand.cs index fde190de50..a8654b5c1f 100644 --- a/src/chocolatey/infrastructure.app/commands/ChocolateyInstallCommand.cs +++ b/src/chocolatey/infrastructure.app/commands/ChocolateyInstallCommand.cs @@ -62,10 +62,10 @@ public virtual void configure_argument_parser(OptionSet optionSet, ChocolateyCon .Add("params=|parameters=|pkgparameters=|packageparameters=|package-parameters=", "PackageParameters - Parameters to pass to the package. Defaults to unspecified.", option => configuration.PackageParameters = option.remove_surrounding_quotes()) - .Add("apply-install-arguments-to-dependencies", + .Add("argsglobal|args-global|installargsglobal|install-args-global|applyargstodependencies|apply-args-to-dependencies|apply-install-arguments-to-dependencies", "Apply Install Arguments To Dependencies - Should install arguments be applied to dependent packages? Defaults to false.", option => configuration.ApplyInstallArgumentsToDependencies = option != null) - .Add("apply-package-parameters-to-dependencies", + .Add("paramsglobal|params-global|packageparametersglobal|package-parameters-global|applyparamstodependencies|apply-params-to-dependencies|apply-package-parameters-to-dependencies", "Apply Package Parameters To Dependencies - Should package parameters be applied to dependent packages? Defaults to false.", option => configuration.ApplyPackageParametersToDependencies = option != null) .Add("allowdowngrade|allow-downgrade", diff --git a/src/chocolatey/infrastructure.app/commands/ChocolateyUninstallCommand.cs b/src/chocolatey/infrastructure.app/commands/ChocolateyUninstallCommand.cs index ae79692f13..b132ada68f 100644 --- a/src/chocolatey/infrastructure.app/commands/ChocolateyUninstallCommand.cs +++ b/src/chocolatey/infrastructure.app/commands/ChocolateyUninstallCommand.cs @@ -58,10 +58,10 @@ public virtual void configure_argument_parser(OptionSet optionSet, ChocolateyCon .Add("params=|parameters=|pkgparameters=|packageparameters=|package-parameters=", "PackageParameters - Parameters to pass to the package. Defaults to unspecified.", option => configuration.PackageParameters = option.remove_surrounding_quotes()) - .Add("apply-install-arguments-to-dependencies", + .Add("argsglobal|args-global|installargsglobal|install-args-global|applyargstodependencies|apply-args-to-dependencies|apply-install-arguments-to-dependencies", "Apply Install Arguments To Dependencies - Should install arguments be applied to dependent packages? Defaults to false.", option => configuration.ApplyInstallArgumentsToDependencies = option != null) - .Add("apply-package-parameters-to-dependencies", + .Add("paramsglobal|params-global|packageparametersglobal|package-parameters-global|applyparamstodependencies|apply-params-to-dependencies|apply-package-parameters-to-dependencies", "Apply Package Parameters To Dependencies - Should package parameters be applied to dependent packages? Defaults to false.", option => configuration.ApplyPackageParametersToDependencies = option != null) .Add("m|sxs|sidebyside|side-by-side|allowmultiple|allow-multiple|allowmultipleversions|allow-multiple-versions", diff --git a/src/chocolatey/infrastructure.app/commands/ChocolateyUpgradeCommand.cs b/src/chocolatey/infrastructure.app/commands/ChocolateyUpgradeCommand.cs index 3c1e7402da..cf3d9f25e3 100644 --- a/src/chocolatey/infrastructure.app/commands/ChocolateyUpgradeCommand.cs +++ b/src/chocolatey/infrastructure.app/commands/ChocolateyUpgradeCommand.cs @@ -63,10 +63,10 @@ public virtual void configure_argument_parser(OptionSet optionSet, ChocolateyCon .Add("params=|parameters=|pkgparameters=|packageparameters=|package-parameters=", "PackageParameters - Parameters to pass to the package. Defaults to unspecified.", option => configuration.PackageParameters = option.remove_surrounding_quotes()) - .Add("apply-install-arguments-to-dependencies", + .Add("argsglobal|args-global|installargsglobal|install-args-global|applyargstodependencies|apply-args-to-dependencies|apply-install-arguments-to-dependencies", "Apply Install Arguments To Dependencies - Should install arguments be applied to dependent packages? Defaults to false.", option => configuration.ApplyInstallArgumentsToDependencies = option != null) - .Add("apply-package-parameters-to-dependencies", + .Add("paramsglobal|params-global|packageparametersglobal|package-parameters-global|applyparamstodependencies|apply-params-to-dependencies|apply-package-parameters-to-dependencies", "Apply Package Parameters To Dependencies - Should package parameters be applied to dependent packages? Defaults to false.", option => configuration.ApplyPackageParametersToDependencies = option != null) .Add("allowdowngrade|allow-downgrade", From 9b20b3361d5e790e42524dcd87310eef80b3f368 Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Fri, 16 Sep 2016 11:06:47 -0500 Subject: [PATCH 2/5] (GH-943) IFileSystem - Open File Exclusively Add method for opening a file exclusively. --- .../infrastructure/filesystem/DotNetFileSystem.cs | 5 +++++ src/chocolatey/infrastructure/filesystem/IFileSystem.cs | 7 +++++++ 2 files changed, 12 insertions(+) diff --git a/src/chocolatey/infrastructure/filesystem/DotNetFileSystem.cs b/src/chocolatey/infrastructure/filesystem/DotNetFileSystem.cs index bdc059edd0..3ce347118c 100644 --- a/src/chocolatey/infrastructure/filesystem/DotNetFileSystem.cs +++ b/src/chocolatey/infrastructure/filesystem/DotNetFileSystem.cs @@ -382,6 +382,11 @@ public FileStream open_file_readonly(string filePath) return File.OpenRead(filePath); } + public FileStream open_file_exclusive(string filePath) + { + return File.Open(filePath,FileMode.OpenOrCreate,FileAccess.ReadWrite,FileShare.None); + } + public void write_file(string filePath, string fileText) { write_file(filePath, fileText, file_exists(filePath) ? get_file_encoding(filePath) : Encoding.UTF8); diff --git a/src/chocolatey/infrastructure/filesystem/IFileSystem.cs b/src/chocolatey/infrastructure/filesystem/IFileSystem.cs index bc86e4cb5d..1a525531c6 100644 --- a/src/chocolatey/infrastructure/filesystem/IFileSystem.cs +++ b/src/chocolatey/infrastructure/filesystem/IFileSystem.cs @@ -238,6 +238,13 @@ public interface IFileSystem /// A file stream object for use after accessing the file FileStream open_file_readonly(string filePath); + /// + /// Opens a file exlusively + /// + /// Path to the file name + /// A file stream object for use after accessing the file + FileStream open_file_exclusive(string filePath); + /// /// Writes the file text to the specified path /// From e818fa32b98dfd148e2a4e31dbf17400f5b7c655 Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Fri, 16 Sep 2016 11:09:59 -0500 Subject: [PATCH 3/5] (GH-943) Lock Pending File Until Operation Completes Open and hold the pending file exclusively open until install is finished. Then remove the file lock. This allows for better concurrent operations when running multiple choco processes at the same time (which isn't necessarily recommended). --- .../services/ChocolateyPackageService.cs | 12 ++++++++++++ .../infrastructure.app/services/FilesService.cs | 3 ++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs b/src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs index c7726d21fa..8ccb1a4a58 100644 --- a/src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs +++ b/src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs @@ -50,6 +50,8 @@ public class ChocolateyPackageService : IChocolateyPackageService private readonly IAutomaticUninstallerService _autoUninstallerService; private readonly IXmlService _xmlService; private readonly IConfigTransformService _configTransformService; + private readonly IDictionary _pendingLocks = new Dictionary(); + private readonly IList _proBusinessMessages = new List { @" Are you ready for the ultimate experience? Check out Pro / Business! @@ -1158,6 +1160,7 @@ public void set_pending(PackageResult packageResult, ChocolateyConfiguration con var pendingFile = _fileSystem.combine_paths(packageDirectory, ApplicationParameters.PackagePendingFileName); _fileSystem.write_file(pendingFile, "{0}".format_with(packageResult.Name)); + _pendingLocks.Add(packageResult.Name.to_lower(), _fileSystem.open_file_exclusive(pendingFile)); } public void remove_pending(PackageResult packageResult, ChocolateyConfiguration config) @@ -1177,6 +1180,15 @@ public void remove_pending(PackageResult packageResult, ChocolateyConfiguration } var pendingFile = _fileSystem.combine_paths(packageDirectory, ApplicationParameters.PackagePendingFileName); + var lockName = packageResult.Name.to_lower(); + if (_pendingLocks.ContainsKey(lockName)) + { + var fileLock = _pendingLocks[lockName]; + _pendingLocks.Remove(lockName); + fileLock.Close(); + fileLock.Dispose(); + } + if (_fileSystem.file_exists(pendingFile)) _fileSystem.delete_file(pendingFile); } diff --git a/src/chocolatey/infrastructure.app/services/FilesService.cs b/src/chocolatey/infrastructure.app/services/FilesService.cs index e2dc6cea6c..8f2b40127c 100644 --- a/src/chocolatey/infrastructure.app/services/FilesService.cs +++ b/src/chocolatey/infrastructure.app/services/FilesService.cs @@ -17,6 +17,7 @@ namespace chocolatey.infrastructure.app.services { using System; using System.IO; + using System.Linq; using configuration; using cryptography; using domain; @@ -117,7 +118,7 @@ public PackageFiles capture_package_files(string directory, ChocolateyConfigurat this.Log().Debug(() => "Capturing package files in '{0}'".format_with(directory)); //gather all files in the folder var files = _fileSystem.get_files(directory, pattern: "*.*", option: SearchOption.AllDirectories); - foreach (string file in files.or_empty_list_if_null()) + foreach (string file in files.or_empty_list_if_null().Where(f => !f.EndsWith(ApplicationParameters.PackagePendingFileName))) { packageFiles.Files.Add(get_package_file(file)); } From 10ad26f542b1c18253f2c54b1625623c11e3b61c Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Fri, 16 Sep 2016 11:11:01 -0500 Subject: [PATCH 4/5] (GH-943) Skip Locked Pending Files When removing packages in a pending state, attempt to open the pending file first. If it fails, log a message about skipping and move on to the next pending file. --- .../tasks/RemovePendingPackagesTask.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/chocolatey/infrastructure.app/tasks/RemovePendingPackagesTask.cs b/src/chocolatey/infrastructure.app/tasks/RemovePendingPackagesTask.cs index 84b234b3cd..c7e507063a 100644 --- a/src/chocolatey/infrastructure.app/tasks/RemovePendingPackagesTask.cs +++ b/src/chocolatey/infrastructure.app/tasks/RemovePendingPackagesTask.cs @@ -72,6 +72,19 @@ private void handle_message(PreRunMessage message) continue; } + try + { + //attempt to open the pending file. If it is locked, continue + var file = _fileSystem.open_file_exclusive(pendingFile); + file.Close(); + file.Dispose(); + } + catch (Exception) + { + this.Log().Debug("Pending file found for {0}, but the file is locked by another process.".format_with(packageFolderName)); + continue; + } + // wait for the file to be at least x seconds old // this allows commands running from the package for configuring sources, etc var fileInfo = _fileSystem.get_file_info_for(pendingFile); From a19831f34603605ec00fd3ff2fa72c579e04149f Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Sat, 17 Sep 2016 11:35:12 -0500 Subject: [PATCH 5/5] (GH-934) Support Paths > 260 Characters Installing a package or working with packaging could mean paths that are over 260 characters (MAX_PATH). Sometimes backing up a path will take it over 260 characters resulting in errors when attempting to upgrade/uninstall packages that have pretty long paths. This is done by using the AlphaFS library. Unfortunately it doesn't use the .NET Framework methods and check lengths before it uses its implementation with native calls so it doesn't appear to be Mono safe. This meant the implementation of IFileSystem that is being used will need to perform those checks and fall back on errors. This also means the addition of dynamics to support switching between two interpretations of FileInfo and DirectoryInfo. --- .../chocolatey.console.csproj | 4 + src/chocolatey.console/packages.config | 1 + src/chocolatey/chocolatey.csproj | 4 + .../tasks/RemovePendingPackagesTask.cs | 2 +- .../filesystem/DotNetFileSystem.cs | 220 ++++++++++++++---- .../infrastructure/filesystem/IFileSystem.cs | 32 +-- src/chocolatey/packages.config | 1 + 7 files changed, 206 insertions(+), 58 deletions(-) diff --git a/src/chocolatey.console/chocolatey.console.csproj b/src/chocolatey.console/chocolatey.console.csproj index b7b24e8d89..bab31e2b48 100644 --- a/src/chocolatey.console/chocolatey.console.csproj +++ b/src/chocolatey.console/chocolatey.console.csproj @@ -78,6 +78,9 @@ app.manifest + + ..\packages\AlphaFS.2.0.1\lib\net40\AlphaFS.dll + ..\packages\log4net.2.0.3\lib\net40-client\log4net.dll @@ -101,6 +104,7 @@ ..\..\lib\PowerShell\System.Management.Automation.dll + diff --git a/src/chocolatey.console/packages.config b/src/chocolatey.console/packages.config index 09f71a5edb..586ded25fb 100644 --- a/src/chocolatey.console/packages.config +++ b/src/chocolatey.console/packages.config @@ -1,6 +1,7 @@  + diff --git a/src/chocolatey/chocolatey.csproj b/src/chocolatey/chocolatey.csproj index 0d497002cf..a617d80c2b 100644 --- a/src/chocolatey/chocolatey.csproj +++ b/src/chocolatey/chocolatey.csproj @@ -38,6 +38,9 @@ ..\..\docs\logo\chocolatey.ico + + ..\packages\AlphaFS.2.0.1\lib\net40\AlphaFS.dll + ..\packages\log4net.2.0.3\lib\net40-client\log4net.dll @@ -73,6 +76,7 @@ ..\packages\Rx-Linq.2.1.30214.0\lib\Net40\System.Reactive.Linq.dll + diff --git a/src/chocolatey/infrastructure.app/tasks/RemovePendingPackagesTask.cs b/src/chocolatey/infrastructure.app/tasks/RemovePendingPackagesTask.cs index c7e507063a..d8e5bfe795 100644 --- a/src/chocolatey/infrastructure.app/tasks/RemovePendingPackagesTask.cs +++ b/src/chocolatey/infrastructure.app/tasks/RemovePendingPackagesTask.cs @@ -63,7 +63,7 @@ private void handle_message(PreRunMessage message) foreach (var pendingFile in pendingFiles.or_empty_list_if_null()) { var packageFolder = _fileSystem.get_directory_name(pendingFile); - var packageFolderName = _fileSystem.get_directory_info_for(packageFolder).Name; + string packageFolderName = _fileSystem.get_directory_info_for(packageFolder).Name; var pendingSkipFiles = _fileSystem.get_files(packageFolder, PENDING_SKIP_FILE, SearchOption.AllDirectories).ToList(); if (pendingSkipFiles.Count != 0) diff --git a/src/chocolatey/infrastructure/filesystem/DotNetFileSystem.cs b/src/chocolatey/infrastructure/filesystem/DotNetFileSystem.cs index 3ce347118c..7c3e30fb36 100644 --- a/src/chocolatey/infrastructure/filesystem/DotNetFileSystem.cs +++ b/src/chocolatey/infrastructure/filesystem/DotNetFileSystem.cs @@ -40,6 +40,8 @@ public sealed class DotNetFileSystem : IFileSystem { private readonly int TIMES_TO_TRY_OPERATION = 3; private static Lazy environment_initializer = new Lazy(() => new Environment()); + private const int MAX_PATH_FILE = 255; + private const int MAX_PATH_DIRECTORY = 248; private void allow_retries(Action action, bool isSilent = false) { @@ -48,7 +50,7 @@ private void allow_retries(Action action, bool isSilent = false) action, waitDurationMilliseconds: 200, increaseRetryByMilliseconds: 100, - isSilent:isSilent); + isSilent: isSilent); } [EditorBrowsable(EditorBrowsableState.Never)] @@ -56,7 +58,7 @@ public void initialize_with(Lazy environment) { environment_initializer = environment; } - + private static IEnvironment Environment { get { return environment_initializer.Value; } @@ -71,14 +73,14 @@ public string combine_paths(string leftItem, params string[] rightItems) var methodName = string.Empty; var stackFrame = new System.Diagnostics.StackFrame(1); if (stackFrame != null) methodName = stackFrame.GetMethod().Name; - throw new ApplicationException("Path to combine cannot be empty. Tried to combine null with '{0}'.{1}".format_with(string.Join(",", rightItems),string.IsNullOrWhiteSpace(methodName) ? string.Empty : " Method called from '{0}'".format_with(methodName))); + throw new ApplicationException("Path to combine cannot be empty. Tried to combine null with '{0}'.{1}".format_with(string.Join(",", rightItems), string.IsNullOrWhiteSpace(methodName) ? string.Empty : " Method called from '{0}'".format_with(methodName))); } var combinedPath = Platform.get_platform() == PlatformType.Windows ? leftItem : leftItem.Replace('\\', '/'); foreach (var rightItem in rightItems) { if (rightItem.Contains(":")) throw new ApplicationException("Cannot combine a path with ':' attempted to combine '{0}' with '{1}'".format_with(rightItem, combinedPath)); - + var rightSide = Platform.get_platform() == PlatformType.Windows ? rightItem : rightItem.Replace('\\', '/'); if (rightSide.StartsWith(Path.DirectorySeparatorChar.to_string()) || rightSide.StartsWith(Path.AltDirectorySeparatorChar.to_string())) { @@ -97,7 +99,14 @@ public string get_full_path(string path) { if (string.IsNullOrWhiteSpace(path)) return path; - return Path.GetFullPath(path); + try + { + return Path.GetFullPath(path); + } + catch (IOException) + { + return Alphaleonis.Win32.Filesystem.Path.GetFullPath(path); + } } public string get_temp_path() @@ -105,7 +114,7 @@ public string get_temp_path() var path = Path.GetTempPath(); if (System.Environment.UserName.contains(ApplicationParameters.Environment.SystemUserName) || path.contains("config\\systemprofile\\appdata")) - { + { path = System.Environment.ExpandEnvironmentVariables(System.Environment.GetEnvironmentVariable(ApplicationParameters.Environment.Temp, EnvironmentVariableTarget.Machine).to_string()); } @@ -131,7 +140,7 @@ public string get_executable_path(string executableName) if (get_file_name_without_extension(executableName).is_equal_to(executableName) && isWindows) { - var pathExtensions = Environment.GetEnvironmentVariable(ApplicationParameters.Environment.PathExtensions).to_string().Split(new[] {ApplicationParameters.Environment.EnvironmentSeparator}, StringSplitOptions.RemoveEmptyEntries); + var pathExtensions = Environment.GetEnvironmentVariable(ApplicationParameters.Environment.PathExtensions).to_string().Split(new[] { ApplicationParameters.Environment.EnvironmentSeparator }, StringSplitOptions.RemoveEmptyEntries); foreach (var extension in pathExtensions.or_empty_list_if_null()) { extensions.Add(extension.StartsWith(".") ? extension : ".{0}".format_with(extension)); @@ -167,7 +176,7 @@ public string get_current_assembly_path() { return Assembly.GetExecutingAssembly().CodeBase.Replace("file:///", string.Empty); } - + #endregion #region File @@ -194,7 +203,14 @@ public IEnumerable get_files(string directoryPath, string[] extensions, public bool file_exists(string filePath) { - return File.Exists(filePath); + try + { + return File.Exists(filePath); + } + catch (IOException) + { + return Alphaleonis.Win32.Filesystem.File.Exists(filePath); + } } public string get_file_name(string filePath) @@ -216,9 +232,21 @@ public string get_file_extension(string filePath) return Path.GetExtension(filePath.Replace('\\', '/')); } - public FileInfo get_file_info_for(string filePath) + public dynamic get_file_info_for(string filePath) { - return new FileInfo(filePath); + try + { + if (!string.IsNullOrWhiteSpace(filePath) && filePath.Length >= MAX_PATH_FILE) + { + return new Alphaleonis.Win32.Filesystem.FileInfo(filePath); + } + + return new FileInfo(filePath); + } + catch (IOException) + { + return new Alphaleonis.Win32.Filesystem.FileInfo(filePath); + } } public System.DateTime get_file_modified_date(string filePath) @@ -236,65 +264,69 @@ public string get_file_version_for(string filePath) return FileVersionInfo.GetVersionInfo(get_full_path(filePath)).FileVersion; } - public bool is_system_file(FileInfo file) + public bool is_system_file(dynamic file) { bool isSystemFile = ((file.Attributes & FileAttributes.System) == FileAttributes.System); if (!isSystemFile) { //check the directory to be sure - DirectoryInfo directoryInfo = get_directory_info_for(file.DirectoryName); + var directoryInfo = get_directory_info_for(file.DirectoryName); isSystemFile = ((directoryInfo.Attributes & FileAttributes.System) == FileAttributes.System); } else { - this.Log().Debug(ChocolateyLoggers.Verbose, () => "File \"{0}\" is a system file.".format_with(file.FullName)); + string fullName = file.FullName; + this.Log().Debug(ChocolateyLoggers.Verbose, () => "File \"{0}\" is a system file.".format_with(fullName)); } return isSystemFile; } - public bool is_readonly_file(FileInfo file) + public bool is_readonly_file(dynamic file) { bool isReadOnlyFile = ((file.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly); if (!isReadOnlyFile) { //check the directory to be sure - DirectoryInfo directoryInfo = get_directory_info_for(file.DirectoryName); + dynamic directoryInfo = get_directory_info_for(file.DirectoryName); isReadOnlyFile = ((directoryInfo.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly); } else { - this.Log().Debug(ChocolateyLoggers.Verbose, () => "File \"{0}\" is a readonly file.".format_with(file.FullName)); + string fullName = file.FullName; + this.Log().Debug(ChocolateyLoggers.Verbose, () => "File \"{0}\" is a readonly file.".format_with(fullName)); } return isReadOnlyFile; } - public bool is_hidden_file(FileInfo file) + public bool is_hidden_file(dynamic file) { bool isHiddenFile = ((file.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden); if (!isHiddenFile) { //check the directory to be sure - DirectoryInfo directoryInfo = get_directory_info_for(file.DirectoryName); + var directoryInfo = get_directory_info_for(file.DirectoryName); isHiddenFile = ((directoryInfo.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden); } else { - this.Log().Debug(ChocolateyLoggers.Verbose, () => "File \"{0}\" is a hidden file.".format_with(file.FullName)); + string fullName = file.FullName; + this.Log().Debug(ChocolateyLoggers.Verbose, () => "File \"{0}\" is a hidden file.".format_with(fullName)); } return isHiddenFile; } - public bool is_encrypted_file(FileInfo file) + public bool is_encrypted_file(dynamic file) { bool isEncrypted = ((file.Attributes & FileAttributes.Encrypted) == FileAttributes.Encrypted); - this.Log().Debug(ChocolateyLoggers.Verbose, () => "Is file \"{0}\" an encrypted file? {1}".format_with(file.FullName, isEncrypted.to_string())); + string fullName = file.FullName; + this.Log().Debug(ChocolateyLoggers.Verbose, () => "Is file \"{0}\" an encrypted file? {1}".format_with(fullName, isEncrypted.to_string())); return isEncrypted; } - public string get_file_date(FileInfo file) + public string get_file_date(dynamic file) { return file.CreationTime < file.LastWriteTime ? file.CreationTime.Date.ToString("yyyyMMdd") @@ -303,7 +335,18 @@ public string get_file_date(FileInfo file) public void move_file(string filePath, string newFilePath) { - allow_retries(() => File.Move(filePath, newFilePath)); + allow_retries( + () => + { + try + { + File.Move(filePath, newFilePath); + } + catch (IOException) + { + Alphaleonis.Win32.Filesystem.File.Move(filePath, newFilePath); + } + }); //Thread.Sleep(10); } @@ -312,7 +355,18 @@ public void copy_file(string sourceFilePath, string destinationFilePath, bool ov this.Log().Debug(ChocolateyLoggers.Verbose, () => "Attempting to copy \"{0}\"{1} to \"{2}\".".format_with(sourceFilePath, Environment.NewLine, destinationFilePath)); create_directory_if_not_exists(get_directory_name(destinationFilePath), ignoreError: true); - allow_retries(() => File.Copy(sourceFilePath, destinationFilePath, overwriteExisting)); + allow_retries( + () => + { + try + { + File.Copy(sourceFilePath, destinationFilePath, overwriteExisting); + } + catch (IOException) + { + Alphaleonis.Win32.Filesystem.File.Copy(sourceFilePath, destinationFilePath, overwriteExisting); + } + }); } public bool copy_file_unsafe(string sourceFilePath, string destinationFilePath, bool overwriteExisting) @@ -331,7 +385,7 @@ public bool copy_file_unsafe(string sourceFilePath, string destinationFilePath, //if (success == 0) //{ // var error = Marshal.GetLastWin32Error(); - + //} return success != 0; } @@ -358,7 +412,18 @@ public void delete_file(string filePath) this.Log().Debug(ChocolateyLoggers.Verbose, () => "Attempting to delete file \"{0}\".".format_with(filePath)); if (file_exists(filePath)) { - allow_retries(() => File.Delete(filePath)); + allow_retries( + () => + { + try + { + File.Delete(filePath); + } + catch (IOException) + { + Alphaleonis.Win32.Filesystem.File.Delete(filePath); + } + }); } } @@ -369,7 +434,14 @@ public FileStream create_file(string filePath) public string read_file(string filePath) { - return File.ReadAllText(filePath, get_file_encoding(filePath)); + try + { + return File.ReadAllText(filePath, get_file_encoding(filePath)); + } + catch (IOException) + { + return Alphaleonis.Win32.Filesystem.File.ReadAllText(filePath, get_file_encoding(filePath)); + } } public byte[] read_file_bytes(string filePath) @@ -384,7 +456,7 @@ public FileStream open_file_readonly(string filePath) public FileStream open_file_exclusive(string filePath) { - return File.Open(filePath,FileMode.OpenOrCreate,FileAccess.ReadWrite,FileShare.None); + return File.Open(filePath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None); } public void write_file(string filePath, string fileText) @@ -447,36 +519,92 @@ public bool directory_exists(string directoryPath) public string get_directory_name(string filePath) { - if (Platform.get_platform() == PlatformType.Windows) return Path.GetDirectoryName(filePath); - - return Path.GetDirectoryName(filePath.Replace('\\', '/')); + if (Platform.get_platform() != PlatformType.Windows && !string.IsNullOrWhiteSpace(filePath)) + { + filePath = filePath.Replace('\\', '/'); + } + + try + { + return Path.GetDirectoryName(filePath); + } + catch (IOException) + { + return Alphaleonis.Win32.Filesystem.Path.GetDirectoryName(filePath); + } } - public DirectoryInfo get_directory_info_for(string directoryPath) + public dynamic get_directory_info_for(string directoryPath) { - return new DirectoryInfo(directoryPath); + try + { + if (!string.IsNullOrWhiteSpace(directoryPath) && directoryPath.Length >= MAX_PATH_DIRECTORY) + { + return new Alphaleonis.Win32.Filesystem.DirectoryInfo(directoryPath); + } + + return new DirectoryInfo(directoryPath); + } + catch (IOException) + { + return new Alphaleonis.Win32.Filesystem.DirectoryInfo(directoryPath); + } } - public DirectoryInfo get_directory_info_from_file_path(string filePath) + public dynamic get_directory_info_from_file_path(string filePath) { - return new DirectoryInfo(filePath).Parent; + try + { + if (!string.IsNullOrWhiteSpace(filePath) && filePath.Length >= MAX_PATH_FILE) + { + return new Alphaleonis.Win32.Filesystem.DirectoryInfo(filePath).Parent; + } + + return new DirectoryInfo(filePath).Parent; + } + catch (IOException) + { + return new Alphaleonis.Win32.Filesystem.DirectoryInfo(filePath).Parent; + } } public void create_directory(string directoryPath) { this.Log().Debug(ChocolateyLoggers.Verbose, () => "Attempting to create directory \"{0}\".".format_with(get_full_path(directoryPath))); - allow_retries(() => Directory.CreateDirectory(directoryPath)); + allow_retries( + () => + { + try + { + Directory.CreateDirectory(directoryPath); + } + catch (IOException) + { + Alphaleonis.Win32.Filesystem.Directory.CreateDirectory(directoryPath); + } + }); } public void move_directory(string directoryPath, string newDirectoryPath) { if (string.IsNullOrWhiteSpace(directoryPath) || string.IsNullOrWhiteSpace(newDirectoryPath)) throw new ApplicationException("You must provide a directory to move from or to."); - if (combine_paths(directoryPath,"").is_equal_to(combine_paths(Environment.GetEnvironmentVariable("SystemDrive"),""))) throw new ApplicationException("Cannot move or delete the root of the system drive"); + if (combine_paths(directoryPath, "").is_equal_to(combine_paths(Environment.GetEnvironmentVariable("SystemDrive"), ""))) throw new ApplicationException("Cannot move or delete the root of the system drive"); try { this.Log().Debug(ChocolateyLoggers.Verbose, "Moving '{0}'{1} to '{2}'".format_with(directoryPath, Environment.NewLine, newDirectoryPath)); - allow_retries(() => Directory.Move(directoryPath, newDirectoryPath)); + allow_retries( + () => + { + try + { + Directory.Move(directoryPath, newDirectoryPath); + } + catch (IOException) + { + Alphaleonis.Win32.Filesystem.Directory.Move(directoryPath, newDirectoryPath); + } + }); } catch (Exception ex) { @@ -568,9 +696,19 @@ public void delete_directory(string directoryPath, bool recursive, bool override } if (!isSilent) this.Log().Debug(ChocolateyLoggers.Verbose, () => "Attempting to delete directory \"{0}\".".format_with(get_full_path(directoryPath))); - allow_retries(() => Directory.Delete(directoryPath, recursive),isSilent: isSilent); + allow_retries( + () => + { + try + { + Directory.Delete(directoryPath, recursive); + } + catch (IOException) + { + Alphaleonis.Win32.Filesystem.Directory.Delete(directoryPath, recursive); + } + }, isSilent: isSilent); } - public void delete_directory_if_exists(string directoryPath, bool recursive) { diff --git a/src/chocolatey/infrastructure/filesystem/IFileSystem.cs b/src/chocolatey/infrastructure/filesystem/IFileSystem.cs index 1a525531c6..4d41005bcc 100644 --- a/src/chocolatey/infrastructure/filesystem/IFileSystem.cs +++ b/src/chocolatey/infrastructure/filesystem/IFileSystem.cs @@ -121,8 +121,8 @@ public interface IFileSystem /// Determines the file information given a path to an existing file /// /// Path to an existing file - /// FileInfo object - FileInfo get_file_info_for(string filePath); + /// FileInfo object or reimplementation of a FileInfo object that works with greater than 260 chars + dynamic get_file_info_for(string filePath); /// /// Gets the file mod date. @@ -148,37 +148,37 @@ public interface IFileSystem /// /// Determines if a file is a system file /// - /// File to check + /// File to check - FileInfo or some representation of FileInfo /// True if the file has the System attribute marked, otherwise false - bool is_system_file(FileInfo file); + bool is_system_file(dynamic file); /// /// Determines if a file is a read only file /// - /// File to check + /// File to check - FileInfo or some representation of FileInfo /// True if the file has the ReadOnly attribute marked, otherwise false - bool is_readonly_file(FileInfo file); + bool is_readonly_file(dynamic file); /// /// Determines if a file is a hidden file /// - /// File to check + /// File to check - FileInfo or some representation of FileInfo /// True if the file has the Hidden attribute marked, otherwise false - bool is_hidden_file(FileInfo file); + bool is_hidden_file(dynamic file); /// /// Determines if a file is encrypted or not /// - /// File to check + /// File to check - FileInfo or some representation of FileInfo /// True if the file has the Encrypted attribute marked, otherwise false - bool is_encrypted_file(FileInfo file); + bool is_encrypted_file(dynamic file); /// /// Determines the older of the file dates, Creation Date or Modified Date /// - /// File to analyze + /// File to analyze - FileInfo or some representation of FileInfo /// The oldest date on the file - string get_file_date(FileInfo file); + string get_file_date(dynamic file); /// /// Moves a specified file to a new location, providing the option to specify a new file name. @@ -311,15 +311,15 @@ public interface IFileSystem /// Returns a DirectoryInfo object from a string /// /// Full path to the directory you want the directory information for - /// DirectoryInfo object - DirectoryInfo get_directory_info_for(string directoryPath); + /// DirectoryInfo object or reimplementation of a DirectoryInfo object that works with greater than 248 chars + dynamic get_directory_info_for(string directoryPath); /// /// Returns a DirectoryInfo object from a string to a filepath /// /// Full path to the file you want directory information for - /// DirectoryInfo object - DirectoryInfo get_directory_info_from_file_path(string filePath); + /// DirectoryInfo object or reimplementation of a DirectoryInfo object that works with greater than 248 chars + dynamic get_directory_info_from_file_path(string filePath); /// /// Creates all directories and subdirectories in the specified path. diff --git a/src/chocolatey/packages.config b/src/chocolatey/packages.config index e3e550ac83..8ad4ed34c2 100644 --- a/src/chocolatey/packages.config +++ b/src/chocolatey/packages.config @@ -1,6 +1,7 @@  +