diff --git a/app/Pog/lib_compiled/Pog/src/Commands/ContainerCommands/InstallFromUrlCommand.cs b/app/Pog/lib_compiled/Pog/src/Commands/ContainerCommands/InstallFromUrlCommand.cs
index 6089670..bdf1262 100644
--- a/app/Pog/lib_compiled/Pog/src/Commands/ContainerCommands/InstallFromUrlCommand.cs
+++ b/app/Pog/lib_compiled/Pog/src/Commands/ContainerCommands/InstallFromUrlCommand.cs
@@ -8,6 +8,7 @@
using Pog.InnerCommands;
using Pog.InnerCommands.Common;
using Pog.Utils;
+using PPaths = Pog.PathConfig.PackagePaths;
namespace Pog.Commands.ContainerCommands;
@@ -35,11 +36,11 @@ public class InstallFromUrlCommand : PogCmdlet {
protected override void BeginProcessing() {
base.BeginProcessing();
_packageDirPath = SessionState.Path.CurrentLocation.ProviderPath;
- _appDirPath = $@"{_packageDirPath}\{PathConfig.PackagePaths.AppDirName}";
- _newAppDirPath = $@"{_packageDirPath}\{PathConfig.PackagePaths.NewAppDirName}";
- _oldAppDirPath = $@"{_packageDirPath}\{PathConfig.PackagePaths.AppBackupDirName}";
- _extractionDirPath = $@"{_packageDirPath}\{PathConfig.PackagePaths.TmpExtractionDirName}";
- _tmpDeletePath = $@"{_packageDirPath}\{PathConfig.PackagePaths.TmpDeleteDirName}";
+ _appDirPath = $@"{_packageDirPath}\{PPaths.AppDirName}";
+ _newAppDirPath = $@"{_packageDirPath}\{PPaths.NewAppDirName}";
+ _oldAppDirPath = $@"{_packageDirPath}\{PPaths.AppBackupDirName}";
+ _extractionDirPath = $@"{_packageDirPath}\{PPaths.TmpExtractionDirName}";
+ _tmpDeletePath = $@"{_packageDirPath}\{PPaths.TmpDeleteDirName}";
// read download parameters from the global container info variable
var internalInfo = Container.ContainerInternalInfo.GetCurrent(this);
@@ -99,7 +100,7 @@ protected override void ProcessRecord() {
PackageInstallParametersArchive pa => pa.Target,
_ => throw new UnreachableException(),
};
- var targetPath = target == null ? _newAppDirPath : JoinValidateSubdirectory(_newAppDirPath, target);
+ var targetPath = target == null ? _newAppDirPath : FsUtils.JoinValidateSubdirectory(_newAppDirPath, target);
if (targetPath == null) {
// the target path escapes from the app directory
@@ -152,21 +153,6 @@ public override void Dispose() {
// (and if it isn't, it probably also won't work here)
}
- /// Assumes that both paths are resolved and cleaned.
- private static bool EscapesDirectory(string basePath, string validatedPath) {
- return !validatedPath.StartsWith(basePath + '\\') && validatedPath != basePath;
- }
-
- /// If `subdirectoryPath` stays inside `basePath`, return the combined path, otherwise return null.
- /// Use this function when resolving an untrusted relative path.
- private static string? JoinValidateSubdirectory(string basePath, string subdirectoryPath) {
- var combined = Path.GetFullPath(basePath + '\\' + subdirectoryPath.TrimEnd('/', '\\'));
- if (EscapesDirectory(basePath, combined)) {
- return null;
- }
- return combined;
- }
-
private void InstallNoArchive(SharedFileCache.IFileLock downloadedFile, string targetPath) {
// ensure that the parent directory exists
Directory.CreateDirectory(Path.GetDirectoryName(targetPath)!);
@@ -182,7 +168,7 @@ private void InstallArchive(PackageInstallParametersArchive param,
InvokePogCommand(new ExpandArchive7Zip(this) {
ArchivePath = downloadedFile.Path,
TargetPath = _extractionDirPath,
- Filter = param.Subdirectory == null ? null : new[] {param.Subdirectory},
+ Filter = param.Subdirectory == null ? null : [param.Subdirectory],
});
}
@@ -242,7 +228,7 @@ private DirectoryInfo GetExtractedSubdirectory(string extractedRootPath, string?
var subPath = resolvedPaths[0]!;
- if (EscapesDirectory(extractedRootPath, subPath)) {
+ if (FsUtils.EscapesDirectory(extractedRootPath, subPath)) {
ThrowTerminatingArgumentError(subdirectory, "SubdirectoryEscapesRoot",
$"Argument passed to the -Subdirectory parameter must be a relative path that does not escape " +
$"the archive directory, got '{subdirectory}'.");
@@ -250,8 +236,6 @@ private DirectoryInfo GetExtractedSubdirectory(string extractedRootPath, string?
// test if the path exists in the extracted directory
var sub = new DirectoryInfo(subPath);
- if ((int) sub.Attributes == -1) {}
-
if ((sub.Attributes & FileAttributes.Directory) == 0) {
// it's actually a file
// use the parent directory, 7zip should have only extracted the file we're interested in
diff --git a/app/Pog/lib_compiled/Pog/src/FsUtils.cs b/app/Pog/lib_compiled/Pog/src/FsUtils.cs
index de33fd1..293c6d2 100644
--- a/app/Pog/lib_compiled/Pog/src/FsUtils.cs
+++ b/app/Pog/lib_compiled/Pog/src/FsUtils.cs
@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.IO;
+using System.IO.Compression;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
@@ -76,6 +78,16 @@ public static bool FileContentEqual(byte[] f1, FileInfo f2) {
return f1.Length == f2.Length && f1.SequenceEqual(File.ReadAllBytes(f2.FullName));
}
+ public static bool FileContentEqual(ZipArchiveEntry f1, FileInfo f2) {
+ if (f1.Length != f2.Length) return false;
+
+ var f1Content = new byte[f1.Length];
+ using var f1Stream = f1.Open();
+ var bytesRead = f1Stream.Read(f1Content, 0, (int) f1.Length);
+ Debug.Assert(bytesRead == f1.Length);
+ return f1Content.SequenceEqual(File.ReadAllBytes(f2.FullName));
+ }
+
public static bool FileContentEqual(FileInfo f1, FileInfo f2) {
// significantly faster than trying to do a streaming implementation
return f1.Length == f2.Length && File.ReadAllBytes(f1.FullName).SequenceEqual(File.ReadAllBytes(f2.FullName));
@@ -169,6 +181,8 @@ public static bool EnsureDeleteFile(string filePath) {
return true;
} catch (FileNotFoundException) {
return false;
+ } catch (DirectoryNotFoundException) {
+ return false; // thrown when parent dir does not exist
}
}
@@ -302,4 +316,20 @@ public static bool IsDirectoryLocked(string directoryPath) {
// move directory to itself; this returns false when the directory contains anything locked, and is a no-op otherwise
return !MoveDirectoryUnlocked(directoryPath, directoryPath);
}
+
+ /// Assumes that both paths are resolved and cleaned.
+ public static bool EscapesDirectory(string basePath, string validatedPath) {
+ return !validatedPath.StartsWith(basePath + '\\') && validatedPath != basePath;
+ }
+
+ /// If `subdirectoryPath` stays inside `basePath`, return the combined path, otherwise return null.
+ /// Assumes that is resolved in canonical form.
+ /// Use this function when resolving an untrusted relative path.
+ public static string? JoinValidateSubdirectory(string basePath, string subdirectoryPath) {
+ var combined = Path.GetFullPath(basePath + '\\' + subdirectoryPath.TrimEnd('/', '\\'));
+ if (EscapesDirectory(basePath, combined)) {
+ return null;
+ }
+ return combined;
+ }
}
diff --git a/app/Pog/lib_compiled/Pog/src/InnerCommands/InvokeCachedFileDownload.cs b/app/Pog/lib_compiled/Pog/src/InnerCommands/InvokeCachedFileDownload.cs
index bb3ae1d..fd7042c 100644
--- a/app/Pog/lib_compiled/Pog/src/InnerCommands/InvokeCachedFileDownload.cs
+++ b/app/Pog/lib_compiled/Pog/src/InnerCommands/InvokeCachedFileDownload.cs
@@ -26,7 +26,7 @@ public enum UserAgentType {
}
}
-public class InvokeCachedFileDownload : ScalarCommand {
+public class InvokeCachedFileDownload(PogCmdlet cmdlet) : ScalarCommand(cmdlet) {
[Parameter(Mandatory = true)] public string SourceUrl = null!;
[Parameter(Mandatory = true)] public string? ExpectedHash;
[Parameter(Mandatory = true)] public DownloadParameters DownloadParameters = null!;
@@ -34,8 +34,6 @@ public class InvokeCachedFileDownload : ScalarCommand
[Parameter] public bool StoreInCache = false;
[Parameter] public CmdletProgressBar.ProgressActivity ProgressActivity = new();
- public InvokeCachedFileDownload(PogCmdlet cmdlet) : base(cmdlet) {}
-
// TODO: handle `InvalidCacheEntryException` everywhere
public override SharedFileCache.IFileLock Invoke() {
Debug.Assert(ExpectedHash == null || !StoreInCache);
@@ -46,7 +44,7 @@ public override SharedFileCache.IFileLock Invoke() {
if (ExpectedHash != null) {
WriteDebug($"Checking if we have a cached copy for '{ExpectedHash}'...");
- var entryLock = GetEntryLockedWithCleanup(ExpectedHash, Package);
+ var entryLock = GetEntryLockedWithCleanup(ExpectedHash);
if (entryLock != null) {
WriteInformation($"File retrieved from the local cache: '{SourceUrl}'");
// do not validate the hash; it was already validated once when the entry was first downloaded;
@@ -108,7 +106,7 @@ private SharedFileCache.CacheEntryLock AddEntryToCache(string hash, SharedFileCa
return InternalState.DownloadCache.AddEntryLocked(hash, entry);
} catch (CacheEntryAlreadyExistsException) {
WriteVerbose("File is already cached.");
- var entryLock = GetEntryLockedWithCleanup(hash, Package);
+ var entryLock = GetEntryLockedWithCleanup(hash);
if (entryLock == null) {
continue; // retry
}
@@ -126,9 +124,9 @@ private SharedFileCache.CacheEntryLock AddEntryToCache(string hash, SharedFileCa
}
}
- private SharedFileCache.CacheEntryLock? GetEntryLockedWithCleanup(string hash, Package package) {
+ private SharedFileCache.CacheEntryLock? GetEntryLockedWithCleanup(string hash) {
try {
- return InternalState.DownloadCache.GetEntryLocked(hash, package);
+ return InternalState.DownloadCache.GetEntryLocked(hash, Package);
} catch (InvalidCacheEntryException) {
WriteWarning($"Found an invalid download cache entry '{hash}', replacing...");
// TODO: figure out how to handle the failure more gracefully (maybe skip the cache all-together
diff --git a/app/Pog/lib_compiled/Pog/src/InnerCommands/InvokeContainer.cs b/app/Pog/lib_compiled/Pog/src/InnerCommands/InvokeContainer.cs
index f95edf1..8b2fd3c 100644
--- a/app/Pog/lib_compiled/Pog/src/InnerCommands/InvokeContainer.cs
+++ b/app/Pog/lib_compiled/Pog/src/InnerCommands/InvokeContainer.cs
@@ -6,7 +6,7 @@
namespace Pog.InnerCommands;
-public class InvokeContainer : EnumerableCommand {
+public class InvokeContainer(PogCmdlet cmdlet) : EnumerableCommand(cmdlet) {
[Parameter(Mandatory = true)] public Container.ContainerType ContainerType;
[Parameter(Mandatory = true)] public Package Package = null!;
[Parameter] public Hashtable? InternalArguments;
@@ -14,8 +14,6 @@ public class InvokeContainer : EnumerableCommand {
private Container? _container;
- public InvokeContainer(PogCmdlet cmdlet) : base(cmdlet) {}
-
public override IEnumerable Invoke() {
_container = new Container(ContainerType, Package, InternalArguments, PackageArguments, Host,
ReadStreamPreferenceVariables());
diff --git a/app/Pog/lib_compiled/Pog/src/Pog.Container.cs b/app/Pog/lib_compiled/Pog/src/Pog.Container.cs
index 6e09b7d..6feb69c 100644
--- a/app/Pog/lib_compiled/Pog/src/Pog.Container.cs
+++ b/app/Pog/lib_compiled/Pog/src/Pog.Container.cs
@@ -34,7 +34,8 @@ public sealed class Container : IDisposable {
/// To read the output streams, use the property after was called.
///
/// Configuration for output stream preference variables.
- public Container(ContainerType containerType, Package package, Hashtable? internalArguments, Hashtable? packageArguments,
+ public Container(ContainerType containerType, Package package,
+ Hashtable? internalArguments, Hashtable? packageArguments,
PSHost? host, OutputStreamConfig streamConfig) {
_containerType = containerType;
_package = package;
@@ -141,7 +142,7 @@ private InitialSessionState CreateInitialSessionState() {
});
var containerDir = InternalState.PathConfig.ContainerDir;
- iss.ImportPSModule(new[] {
+ iss.ImportPSModule([
// these two imports contain basic stuff needed for printing output, errors, FS traversal,...
"Microsoft.PowerShell.Management",
"Microsoft.PowerShell.Utility",
@@ -154,7 +155,7 @@ private InitialSessionState CreateInitialSessionState() {
ContainerType.GetInstallHash => $@"{containerDir}\Install\Env_GetInstallHash.psm1",
_ => throw new ArgumentOutOfRangeException(nameof(_containerType), _containerType, null),
},
- });
+ ]);
// TODO: figure out if we can define this without having to write inline PowerShell function
// override Import-Module to hide the default verbose prints when -Verbose is set for the container environment
diff --git a/app/Pog/lib_compiled/Pog/src/Pog.SharedFileCache.cs b/app/Pog/lib_compiled/Pog/src/Pog.SharedFileCache.cs
index 8202bef..c7856cb 100644
--- a/app/Pog/lib_compiled/Pog/src/Pog.SharedFileCache.cs
+++ b/app/Pog/lib_compiled/Pog/src/Pog.SharedFileCache.cs
@@ -45,17 +45,12 @@ public class CacheEntryInUseException(string entryKey)
// - second one has any other name, and is the actual cache entry; during insertion, if the entry file is also called
// `referencingPackages.json-list`, it is prefixed with `_`
[PublicAPI]
-public class SharedFileCache {
+public class SharedFileCache(string cacheDirPath, TmpDirectory tmpDir) {
private const string MetadataFileName = "referencingPackages.json-list";
- public readonly string Path;
+ public readonly string Path = cacheDirPath;
/// Directory for temporary files on the same volume as `.Path`, used for adding and removing entries.
- private readonly TmpDirectory _tmpDirectory;
-
- public SharedFileCache(string cacheDirPath, TmpDirectory tmpDir) {
- Path = cacheDirPath;
- _tmpDirectory = tmpDir;
- }
+ private readonly TmpDirectory _tmpDirectory = tmpDir;
public delegate void InvalidCacheEntryCb(InvalidCacheEntryException exception);
diff --git a/app/Pog/lib_compiled/Pog/src/Pog.TmpDirectory.cs b/app/Pog/lib_compiled/Pog/src/Pog.TmpDirectory.cs
index 3108771..16eb905 100644
--- a/app/Pog/lib_compiled/Pog/src/Pog.TmpDirectory.cs
+++ b/app/Pog/lib_compiled/Pog/src/Pog.TmpDirectory.cs
@@ -2,7 +2,6 @@
using System.Diagnostics;
using System.IO;
using JetBrains.Annotations;
-using IOPath = System.IO.Path;
namespace Pog;
@@ -11,15 +10,11 @@ namespace Pog;
/// because the directory must be at the same partition as the download cache.
///
[PublicAPI]
-public class TmpDirectory {
- public readonly string Path;
-
- public TmpDirectory(string tmpDirPath) {
- Path = tmpDirPath;
- }
+public class TmpDirectory(string tmpDirPath) {
+ public readonly string Path = tmpDirPath;
public string GetTemporaryPath() {
- var path = IOPath.Combine(Path, Guid.NewGuid().ToString());
+ var path = $"{Path}\\{Guid.NewGuid()}";
// the chance is very small...
Debug.Assert(!Directory.Exists(path) && !File.Exists(path));
return path;
diff --git a/app/Pog/lib_compiled/Pog/src/Utils/FileSystemInfoExtensions.cs b/app/Pog/lib_compiled/Pog/src/Utils/FileSystemInfoExtensions.cs
index c8e04db..cf1dc50 100644
--- a/app/Pog/lib_compiled/Pog/src/Utils/FileSystemInfoExtensions.cs
+++ b/app/Pog/lib_compiled/Pog/src/Utils/FileSystemInfoExtensions.cs
@@ -2,7 +2,7 @@
namespace Pog.Utils;
-public static class FileSystemInfoExtensions {
+internal static class FileSystemInfoExtensions {
public static string GetBaseName(this FileSystemInfo info) {
return Path.GetFileNameWithoutExtension(info.Name);
}
diff --git a/app/Pog/lib_compiled/Pog/src/Utils/IEnumerableExtensions.cs b/app/Pog/lib_compiled/Pog/src/Utils/IEnumerableExtensions.cs
index 080e568..1160c08 100644
--- a/app/Pog/lib_compiled/Pog/src/Utils/IEnumerableExtensions.cs
+++ b/app/Pog/lib_compiled/Pog/src/Utils/IEnumerableExtensions.cs
@@ -4,7 +4,7 @@
namespace Pog.Utils;
-public static class EnumerableExtensions {
+internal static class EnumerableExtensions {
public static IEnumerable SelectOptional(this IEnumerable enumerable, Func selector)
where TOut : class {
return enumerable.Select(selector).WhereNotNull();
diff --git a/app/Pog/lib_compiled/Pog/src/Utils/KeyValuePairExtensions.cs b/app/Pog/lib_compiled/Pog/src/Utils/KeyValuePairExtensions.cs
index 468609e..a61cca3 100644
--- a/app/Pog/lib_compiled/Pog/src/Utils/KeyValuePairExtensions.cs
+++ b/app/Pog/lib_compiled/Pog/src/Utils/KeyValuePairExtensions.cs
@@ -2,7 +2,7 @@
namespace Pog.Utils;
-public static class KeyValuePairExtensions {
+internal static class KeyValuePairExtensions {
public static void Deconstruct(this KeyValuePair keyValuePair,
out TKey key, out TValue value) {
key = keyValuePair.Key;
diff --git a/app/Pog/lib_compiled/Pog/src/Utils/LazyDisposable.cs b/app/Pog/lib_compiled/Pog/src/Utils/LazyDisposable.cs
index af5b63d..839bede 100644
--- a/app/Pog/lib_compiled/Pog/src/Utils/LazyDisposable.cs
+++ b/app/Pog/lib_compiled/Pog/src/Utils/LazyDisposable.cs
@@ -7,7 +7,7 @@ namespace Pog.Utils;
/// A object that implements .
/// The object being lazily created.
-public sealed class LazyDisposable : Lazy, IDisposable where T : IDisposable {
+internal sealed class LazyDisposable : Lazy, IDisposable where T : IDisposable {
///
/// Initializes a new instance of the class.
/// When lazy initialization occurs, the default constructor is used.