mirror of
https://github.com/k1tbyte/Wand-Enhancer.git
synced 2026-09-03 13:00:44 +00:00
fix(asar): correct archive tree lookups and fail loudly on unreadable input
InsertFile resolved the grandparent node instead of the parent. Reads no longer create phantom directories in the header. Bound symlink traversal and skip reparse points when crawling. Locked or unreadable files now abort packing instead of being dropped. Read headers and integrity blocks with a full-read loop. Assert the header keeps its size before overwriting the placeholder. Validate Pickle buffer sizes, payload overflow and negative lengths. Check CreateSymbolicLink and external tool exit codes. Drop unused Pickle accessors, TransformedFile and FilesystemFilesAndLinks.Links.
This commit is contained in:
@@ -73,22 +73,26 @@ namespace AsarSharp
|
||||
filesystem.InsertFile(filename, shouldUnpack, file, placeholder);
|
||||
break;
|
||||
case FileType.Link:
|
||||
throw new NotImplementedException();
|
||||
throw new NotSupportedException($"Packing symlinks is not supported: '{filename}'");
|
||||
}
|
||||
}
|
||||
|
||||
private bool ShouldUnpackPath(string relativePath)
|
||||
/// <summary>
|
||||
/// Matches the directory path (relative to the archive root) against the unpack regex.
|
||||
/// </summary>
|
||||
private bool ShouldUnpackPath(string relativeParentPath)
|
||||
{
|
||||
return _options?.Unpack?.IsMatch(relativePath) == true;
|
||||
return _options?.Unpack?.IsMatch(relativeParentPath) == true;
|
||||
}
|
||||
|
||||
private void InsertsDone(Filesystem filesystem, List<Disk.BasicFileInfo> files)
|
||||
{
|
||||
Directory.CreateDirectory(
|
||||
Path.GetDirectoryName(_destPath)
|
||||
?? throw new InvalidOperationException());
|
||||
string dir = Path.GetDirectoryName(_destPath);
|
||||
if (!string.IsNullOrEmpty(dir))
|
||||
Directory.CreateDirectory(dir);
|
||||
|
||||
Disk.WriteFileSystem(_destPath, filesystem,
|
||||
new Disk.FilesystemFilesAndLinks { Files = files, Links = null }, _metadata);
|
||||
new Disk.FilesystemFilesAndLinks { Files = files }, _metadata);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,13 +159,6 @@ namespace AsarSharp
|
||||
FilesystemEntry file, HashSet<string> dirCache)
|
||||
{
|
||||
var linkSrcPath = Extensions.GetDirectoryName(Path.Combine(dest, file.Link));
|
||||
var linkDestPath = Extensions.GetDirectoryName(destFilename);
|
||||
var relativeLinkPath = Extensions.GetRelativePath(linkDestPath, linkSrcPath);
|
||||
|
||||
try { File.Delete(destFilename); }
|
||||
catch { /* ignore — failing to remove an existing link is non-fatal */ }
|
||||
|
||||
var linkTo = Path.Combine(relativeLinkPath, Path.GetFileName(file.Link));
|
||||
|
||||
if (!Extensions.IsPathInside(dest, linkSrcPath))
|
||||
{
|
||||
@@ -173,6 +166,12 @@ namespace AsarSharp
|
||||
$"{fullPath}: file \"{file.Link}\" links out of the package to \"{linkSrcPath}\"");
|
||||
}
|
||||
|
||||
try { File.Delete(destFilename); }
|
||||
catch (Exception e) when (e is IOException || e is UnauthorizedAccessException)
|
||||
{
|
||||
// Nothing to replace, or the old entry is locked; the copy below reports the real failure.
|
||||
}
|
||||
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
var targetPath = Path.Combine(linkSrcPath, Path.GetFileName(file.Link));
|
||||
@@ -189,8 +188,10 @@ namespace AsarSharp
|
||||
}
|
||||
else
|
||||
{
|
||||
var linkDestPath = Extensions.GetDirectoryName(destFilename);
|
||||
var relativeLinkPath = Extensions.GetRelativePath(linkDestPath, linkSrcPath);
|
||||
EnsureParentDir(destFilename, dirCache);
|
||||
Extensions.CreateSymbolicLink(linkTo, destFilename);
|
||||
Extensions.CreateSymbolicLink(Path.Combine(relativeLinkPath, Path.GetFileName(file.Link)), destFilename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,8 +12,6 @@ namespace AsarSharp.AsarFileSystem
|
||||
public static class Disk
|
||||
{
|
||||
private const int StreamBufferSize = 1024 * 1024;
|
||||
private static readonly ConcurrentDictionary<string, Filesystem> _filesystemCache =
|
||||
new ConcurrentDictionary<string, Filesystem>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
public class ArchiveHeader
|
||||
{
|
||||
@@ -25,7 +23,6 @@ namespace AsarSharp.AsarFileSystem
|
||||
public class FilesystemFilesAndLinks
|
||||
{
|
||||
public List<BasicFileInfo> Files { get; set; } = new List<BasicFileInfo>();
|
||||
public List<BasicFileInfo> Links { get; set; } = new List<BasicFileInfo>();
|
||||
}
|
||||
|
||||
public class BasicFileInfo
|
||||
@@ -42,14 +39,14 @@ namespace AsarSharp.AsarFileSystem
|
||||
65536, FileOptions.SequentialScan))
|
||||
{
|
||||
byte[] sizeBuf = new byte[8];
|
||||
if (fs.Read(sizeBuf, 0, 8) != 8)
|
||||
if (fs.ReadFull(sizeBuf, 0, 8) != 8)
|
||||
throw new Exception("Unable to read header size");
|
||||
|
||||
var sizePickle = Pickle.CreateFromBuffer(sizeBuf);
|
||||
var size = sizePickle.CreateIterator().ReadUInt32();
|
||||
|
||||
var headerBuf = new byte[size];
|
||||
if (fs.Read(headerBuf, 0, (int)size) != size)
|
||||
if (fs.ReadFull(headerBuf, 0, (int)size) != size)
|
||||
throw new Exception("Unable to read header");
|
||||
|
||||
var headerPickle = Pickle.CreateFromBuffer(headerBuf);
|
||||
@@ -65,62 +62,28 @@ namespace AsarSharp.AsarFileSystem
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the header fresh every time: an archive is repacked in place during a patch run,
|
||||
/// so a cached header would hand out stale offsets on the next read of the same path.
|
||||
/// </summary>
|
||||
public static Filesystem ReadFilesystemSync(string archivePath)
|
||||
{
|
||||
return _filesystemCache.GetOrAdd(archivePath, key =>
|
||||
{
|
||||
var header = ReadArchiveHeaderSync(key);
|
||||
var filesystem = new Filesystem(key);
|
||||
filesystem.SetHeader(header.Header, header.HeaderSize);
|
||||
return filesystem;
|
||||
});
|
||||
}
|
||||
|
||||
public static byte[] ReadFileSync(Filesystem filesystem, string filename, FilesystemEntry info)
|
||||
{
|
||||
if (!info.IsFile || !info.Size.HasValue)
|
||||
throw new ArgumentException("Entry is not a file", nameof(info));
|
||||
|
||||
long size = info.Size.Value;
|
||||
byte[] buffer = new byte[size];
|
||||
|
||||
if (size <= 0) return buffer;
|
||||
|
||||
if (info.Unpacked == true)
|
||||
{
|
||||
string filePath = Path.Combine($"{filesystem.GetRootPath()}.unpacked", filename);
|
||||
return File.ReadAllBytes(filePath);
|
||||
}
|
||||
|
||||
using (var fs = new FileStream(filesystem.GetRootPath(), FileMode.Open, FileAccess.Read,
|
||||
FileShare.Read, 65536, FileOptions.RandomAccess))
|
||||
{
|
||||
long offset = 8 + filesystem.GetHeaderSize() + long.Parse(info.Offset);
|
||||
fs.Position = offset;
|
||||
int bytesRead = fs.Read(buffer, 0, (int)size);
|
||||
if (bytesRead != size)
|
||||
throw new Exception($"Failed to read entire file, got {bytesRead} bytes instead of {size}");
|
||||
}
|
||||
|
||||
return buffer;
|
||||
var header = ReadArchiveHeaderSync(archivePath);
|
||||
var filesystem = new Filesystem(archivePath);
|
||||
filesystem.SetHeader(header.Header, header.HeaderSize);
|
||||
return filesystem;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public static bool UncacheFilesystem(string archivePath)
|
||||
{
|
||||
return _filesystemCache.TryRemove(archivePath, out _);
|
||||
}
|
||||
|
||||
public static void UncacheAll()
|
||||
{
|
||||
_filesystemCache.Clear();
|
||||
}
|
||||
|
||||
public static void CopyFile(string dest, string rootPath, string filename)
|
||||
{
|
||||
if (dest == null || rootPath == null || filename == null)
|
||||
throw new ArgumentNullException();
|
||||
if (dest == null)
|
||||
throw new ArgumentNullException(nameof(dest));
|
||||
if (rootPath == null)
|
||||
throw new ArgumentNullException(nameof(rootPath));
|
||||
if (filename == null)
|
||||
throw new ArgumentNullException(nameof(filename));
|
||||
|
||||
string normalizedDestRoot = Path.GetFullPath(dest)
|
||||
.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
|
||||
@@ -192,6 +155,18 @@ namespace AsarSharp.AsarFileSystem
|
||||
var patchedSizePickle = Pickle.CreateEmpty();
|
||||
patchedSizePickle.WriteUInt32((uint)patchedPickle.GetTotalSize());
|
||||
|
||||
// The rewrite lands on top of the placeholder header, so it must be exactly as
|
||||
// long. Placeholder hashes are the same width as real ones, so this holds unless
|
||||
// a file changed size between crawl and write - which would silently shred the
|
||||
// payload that follows.
|
||||
if (patchedPickle.GetTotalSize() != headerPickle.GetTotalSize() ||
|
||||
patchedSizePickle.GetTotalSize() != sizePickleSize)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"ASAR header changed size while packing (a source file was modified mid-build). " +
|
||||
"Aborting rather than writing a corrupt archive.");
|
||||
}
|
||||
|
||||
fs.Position = 0;
|
||||
patchedSizePickle.WriteTo(fs);
|
||||
patchedPickle.WriteTo(fs);
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace AsarSharp.AsarFileSystem
|
||||
_headerSize = headerSize;
|
||||
}
|
||||
|
||||
public FilesystemEntry SearchNodeFromDirectory(string p)
|
||||
public FilesystemEntry SearchNodeFromDirectory(string p, bool create = true)
|
||||
{
|
||||
FilesystemEntry json = _header;
|
||||
|
||||
@@ -59,12 +59,31 @@ namespace AsarSharp.AsarFileSystem
|
||||
string seg = p.Substring(start, segLen);
|
||||
|
||||
if (!json.IsDirectory)
|
||||
throw new Exception($"Unexpected directory state while traversing: {p}");
|
||||
{
|
||||
if (create)
|
||||
throw new Exception($"Unexpected directory state while traversing: {p}");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (json.Files == null)
|
||||
{
|
||||
if (create)
|
||||
json.Files = new Dictionary<string, FilesystemEntry>(StringComparer.Ordinal);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!json.Files.TryGetValue(seg, out var child))
|
||||
{
|
||||
child = new FilesystemEntry { Files = new Dictionary<string, FilesystemEntry>(StringComparer.Ordinal) };
|
||||
json.Files[seg] = child;
|
||||
if (create)
|
||||
{
|
||||
child = new FilesystemEntry { Files = new Dictionary<string, FilesystemEntry>(StringComparer.Ordinal) };
|
||||
json.Files[seg] = child;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
json = child;
|
||||
start = end + 1;
|
||||
@@ -81,7 +100,7 @@ namespace AsarSharp.AsarFileSystem
|
||||
|
||||
string name = Path.GetFileName(rel);
|
||||
string dir = Extensions.GetDirectoryName(rel);
|
||||
var parent = SearchNodeFromDirectory(dir);
|
||||
var parent = SearchNodeFromDirectory(dir, true);
|
||||
|
||||
if (parent.Files == null)
|
||||
parent.Files = new Dictionary<string, FilesystemEntry>(StringComparer.Ordinal);
|
||||
@@ -111,18 +130,23 @@ namespace AsarSharp.AsarFileSystem
|
||||
}
|
||||
}
|
||||
|
||||
public FilesystemEntry GetNode(string p, bool followLinks = true)
|
||||
public FilesystemEntry GetNode(string p, bool followLinks = true, int linkDepth = 0)
|
||||
{
|
||||
if (linkDepth > 40)
|
||||
throw new Exception($"Symlink loop detected at {p}");
|
||||
|
||||
p = p.Replace('/', Path.DirectorySeparatorChar).Replace('\\', Path.DirectorySeparatorChar);
|
||||
FilesystemEntry node = SearchNodeFromDirectory(Extensions.GetDirectoryName(p));
|
||||
FilesystemEntry node = SearchNodeFromDirectory(Extensions.GetDirectoryName(p), false);
|
||||
if (node == null)
|
||||
return null;
|
||||
string name = Path.GetFileName(p);
|
||||
|
||||
if (node.IsLink && followLinks)
|
||||
return GetNode(Path.Combine(node.Link, name));
|
||||
return GetNode(Path.Combine(node.Link, name), followLinks, linkDepth + 1);
|
||||
|
||||
if (!string.IsNullOrEmpty(name))
|
||||
{
|
||||
if (node.IsDirectory && node.Files.TryGetValue(name, out var entry))
|
||||
if (node.IsDirectory && node.Files != null && node.Files.TryGetValue(name, out var entry))
|
||||
return entry;
|
||||
return null;
|
||||
}
|
||||
@@ -130,16 +154,17 @@ namespace AsarSharp.AsarFileSystem
|
||||
return node;
|
||||
}
|
||||
|
||||
public FilesystemEntry GetFile(string p, bool followLinks = true)
|
||||
public FilesystemEntry GetFile(string p, bool followLinks = true, int linkDepth = 0)
|
||||
{
|
||||
FilesystemEntry info = GetNode(p, followLinks);
|
||||
if (linkDepth > 40)
|
||||
throw new Exception($"Symlink loop detected at {p}");
|
||||
|
||||
FilesystemEntry info = GetNode(p, followLinks, linkDepth);
|
||||
if (info == null) throw new Exception($"\"{p}\" was not found in this archive");
|
||||
if (info.IsLink && followLinks) return GetFile(info.Link, followLinks);
|
||||
if (info.IsLink && followLinks) return GetFile(info.Link, followLinks, linkDepth + 1);
|
||||
return info;
|
||||
}
|
||||
|
||||
public static string ReadLink(string path) => throw new NotImplementedException();
|
||||
|
||||
#region Writing
|
||||
|
||||
public FilesystemEntry SearchNodeFromPath(string p)
|
||||
@@ -159,7 +184,7 @@ namespace AsarSharp.AsarFileSystem
|
||||
public void InsertFile(string path, bool shouldUnpack, CrawledFileType file,
|
||||
IntegrityHelper.FileIntegrity precomputedIntegrity = null)
|
||||
{
|
||||
var (dirNode, _) = SearchNodeFromPathWithParent(Path.GetDirectoryName(path) ?? path);
|
||||
var (dirNode, _) = SearchNodeFromPathWithParent(path);
|
||||
var node = SearchNodeFromPath(path);
|
||||
|
||||
long size;
|
||||
|
||||
@@ -9,13 +9,6 @@ namespace AsarSharp.AsarFileSystem
|
||||
{
|
||||
public FileType Type { get; set; }
|
||||
public FileSystemInfo Stat { get; set; }
|
||||
public TransformedFile Transformed { get; set; }
|
||||
}
|
||||
|
||||
public class TransformedFile
|
||||
{
|
||||
public string Path { get; set; }
|
||||
public FileSystemInfo Stat { get; set; }
|
||||
}
|
||||
|
||||
public enum FileType
|
||||
@@ -36,7 +29,7 @@ namespace AsarSharp.AsarFileSystem
|
||||
}
|
||||
catch (Exception ex) when (ex is IOException || ex is UnauthorizedAccessException)
|
||||
{
|
||||
return null;
|
||||
throw new IOException($"Failed to read attributes for '{filename}'", ex);
|
||||
}
|
||||
|
||||
bool isDirectory = (attributes & FileAttributes.Directory) == FileAttributes.Directory;
|
||||
@@ -59,7 +52,6 @@ namespace AsarSharp.AsarFileSystem
|
||||
foreach (var fullPath in CrawlIterative(dir))
|
||||
{
|
||||
var type = DetermineFileType(fullPath);
|
||||
if (type == null) continue;
|
||||
metadata[fullPath] = type;
|
||||
if (type.Type == FileType.Link) links.Add(fullPath);
|
||||
filenames.Add(fullPath);
|
||||
@@ -77,7 +69,8 @@ namespace AsarSharp.AsarFileSystem
|
||||
{
|
||||
if (string.Equals(filename, link, StringComparison.OrdinalIgnoreCase)) continue;
|
||||
|
||||
if (filename.StartsWith(link, StringComparison.OrdinalIgnoreCase))
|
||||
// Require a separator after the prefix so "…/foobar" does not match link "…/foo".
|
||||
if (filename.StartsWith(link + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
string rel = Extensions.GetRelativePath(link, fileDir);
|
||||
if (!rel.StartsWith("..", StringComparison.Ordinal))
|
||||
@@ -120,7 +113,7 @@ namespace AsarSharp.AsarFileSystem
|
||||
foreach (var entry in entries)
|
||||
{
|
||||
result.Add(entry.FullName);
|
||||
if (entry is DirectoryInfo subDir)
|
||||
if (entry is DirectoryInfo subDir && (subDir.Attributes & FileAttributes.ReparsePoint) == 0)
|
||||
stack.Push(subDir);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using AsarSharp.Utils;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace AsarSharp.Integrity
|
||||
@@ -60,7 +61,9 @@ namespace AsarSharp.Integrity
|
||||
var blockHashes = new List<string>(estimatedBlockCount);
|
||||
int bytesRead;
|
||||
|
||||
while ((bytesRead = fileStream.Read(reusableBuffer, 0, reusableBuffer.Length)) > 0)
|
||||
// ReadFull, not Read: a short read would hash a partial block and produce
|
||||
// integrity blocks Electron rejects.
|
||||
while ((bytesRead = fileStream.ReadFull(reusableBuffer, 0, reusableBuffer.Length)) > 0)
|
||||
{
|
||||
blockHashes.Add(ToLowerHex(blockHash.ComputeHash(reusableBuffer, 0, bytesRead)));
|
||||
fileHash.AppendData(reusableBuffer, 0, bytesRead);
|
||||
|
||||
@@ -28,16 +28,18 @@ namespace AsarSharp.PickleTools
|
||||
{
|
||||
if (buffer != null)
|
||||
{
|
||||
if (buffer.Length < SIZE_UINT32)
|
||||
throw new ArgumentException("Buffer is too small.", nameof(buffer));
|
||||
|
||||
_header = buffer;
|
||||
_headerSize = buffer.Length - GetPayloadSize();
|
||||
int payloadSize = GetPayloadSize();
|
||||
if (payloadSize > buffer.Length)
|
||||
throw new ArgumentException("Payload size exceeds buffer length.", nameof(buffer));
|
||||
|
||||
_headerSize = buffer.Length - payloadSize;
|
||||
_capacityAfterHeader = CAPACITY_READ_ONLY;
|
||||
_writeOffset = 0;
|
||||
|
||||
if (_headerSize > buffer.Length)
|
||||
{
|
||||
_headerSize = 0;
|
||||
}
|
||||
|
||||
if (_headerSize != AlignInt(_headerSize, SIZE_UINT32))
|
||||
{
|
||||
_headerSize = 0;
|
||||
@@ -86,7 +88,7 @@ namespace AsarSharp.PickleTools
|
||||
}
|
||||
|
||||
|
||||
public bool WriteBool(bool value) => WriteInt(value ? 1 : 0);
|
||||
|
||||
|
||||
public bool WriteInt(int value)
|
||||
{
|
||||
@@ -121,74 +123,7 @@ namespace AsarSharp.PickleTools
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool WriteInt64(long value)
|
||||
{
|
||||
const int dataLength = SIZE_INT64;
|
||||
int newSize = _writeOffset + dataLength;
|
||||
|
||||
if (newSize > _capacityAfterHeader)
|
||||
{
|
||||
Resize(Math.Max((int)_capacityAfterHeader * 2, newSize));
|
||||
}
|
||||
|
||||
WriteInt64LE(value, _headerSize + _writeOffset);
|
||||
SetPayloadSize(newSize);
|
||||
_writeOffset = newSize;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public bool WriteUInt64(ulong value)
|
||||
{
|
||||
const int dataLength = SIZE_UINT64;
|
||||
int newSize = _writeOffset + dataLength;
|
||||
|
||||
if (newSize > _capacityAfterHeader)
|
||||
{
|
||||
Resize(Math.Max((int)_capacityAfterHeader * 2, newSize));
|
||||
}
|
||||
|
||||
WriteUInt64LE(value, _headerSize + _writeOffset);
|
||||
SetPayloadSize(newSize);
|
||||
_writeOffset = newSize;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool WriteFloat(float value)
|
||||
{
|
||||
const int dataLength = SIZE_FLOAT;
|
||||
int newSize = _writeOffset + dataLength;
|
||||
|
||||
if (newSize > _capacityAfterHeader)
|
||||
{
|
||||
Resize(Math.Max((int)_capacityAfterHeader * 2, newSize));
|
||||
}
|
||||
|
||||
int bits = BitConverter.ToInt32(BitConverter.GetBytes(value), 0);
|
||||
WriteInt32LE(bits, _headerSize + _writeOffset);
|
||||
|
||||
SetPayloadSize(newSize);
|
||||
_writeOffset = newSize;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool WriteDouble(double value)
|
||||
{
|
||||
const int dataLength = SIZE_DOUBLE;
|
||||
int newSize = _writeOffset + dataLength;
|
||||
|
||||
if (newSize > _capacityAfterHeader)
|
||||
{
|
||||
Resize(Math.Max((int)_capacityAfterHeader * 2, newSize));
|
||||
}
|
||||
|
||||
long bits = BitConverter.DoubleToInt64Bits(value);
|
||||
WriteInt64LE(bits, _headerSize + _writeOffset);
|
||||
|
||||
SetPayloadSize(newSize);
|
||||
_writeOffset = newSize;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool WriteString(string value)
|
||||
{
|
||||
@@ -226,7 +161,13 @@ namespace AsarSharp.PickleTools
|
||||
WriteUInt32LE((uint)payloadSize, 0);
|
||||
}
|
||||
|
||||
public int GetPayloadSize() => (int)ReadUInt32LE(0);
|
||||
public int GetPayloadSize()
|
||||
{
|
||||
uint size = ReadUInt32LE(0);
|
||||
if (size > int.MaxValue)
|
||||
throw new InvalidOperationException("Payload size exceeds maximum allowed (2GB).");
|
||||
return (int)size;
|
||||
}
|
||||
|
||||
private void Resize(int newCapacity)
|
||||
{
|
||||
@@ -275,29 +216,7 @@ namespace AsarSharp.PickleTools
|
||||
_header[offset + 3] = (byte)(value >> 24);
|
||||
}
|
||||
|
||||
private void WriteInt64LE(long value, int offset)
|
||||
{
|
||||
_header[offset] = (byte)value;
|
||||
_header[offset + 1] = (byte)(value >> 8);
|
||||
_header[offset + 2] = (byte)(value >> 16);
|
||||
_header[offset + 3] = (byte)(value >> 24);
|
||||
_header[offset + 4] = (byte)(value >> 32);
|
||||
_header[offset + 5] = (byte)(value >> 40);
|
||||
_header[offset + 6] = (byte)(value >> 48);
|
||||
_header[offset + 7] = (byte)(value >> 56);
|
||||
}
|
||||
|
||||
private void WriteUInt64LE(ulong value, int offset)
|
||||
{
|
||||
_header[offset] = (byte)value;
|
||||
_header[offset + 1] = (byte)(value >> 8);
|
||||
_header[offset + 2] = (byte)(value >> 16);
|
||||
_header[offset + 3] = (byte)(value >> 24);
|
||||
_header[offset + 4] = (byte)(value >> 32);
|
||||
_header[offset + 5] = (byte)(value >> 40);
|
||||
_header[offset + 6] = (byte)(value >> 48);
|
||||
_header[offset + 7] = (byte)(value >> 56);
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -18,10 +18,7 @@ namespace AsarSharp.PickleTools
|
||||
_endIndex = pickle.GetPayloadSize();
|
||||
}
|
||||
|
||||
public bool ReadBool()
|
||||
{
|
||||
return ReadInt() != 0;
|
||||
}
|
||||
|
||||
|
||||
public int ReadInt()
|
||||
{
|
||||
@@ -33,25 +30,7 @@ namespace AsarSharp.PickleTools
|
||||
return ReadBytes(Pickle.SIZE_UINT32, BitConverter.ToUInt32);
|
||||
}
|
||||
|
||||
public long ReadInt64()
|
||||
{
|
||||
return ReadBytes(Pickle.SIZE_INT64, BitConverter.ToInt64);
|
||||
}
|
||||
|
||||
public ulong ReadUInt64()
|
||||
{
|
||||
return ReadBytes(Pickle.SIZE_UINT64, BitConverter.ToUInt64);
|
||||
}
|
||||
|
||||
public float ReadFloat()
|
||||
{
|
||||
return ReadBytes(Pickle.SIZE_FLOAT, BitConverter.ToSingle);
|
||||
}
|
||||
|
||||
public double ReadDouble()
|
||||
{
|
||||
return ReadBytes(Pickle.SIZE_DOUBLE, BitConverter.ToDouble);
|
||||
}
|
||||
|
||||
public string ReadString()
|
||||
{
|
||||
@@ -75,7 +54,7 @@ namespace AsarSharp.PickleTools
|
||||
|
||||
private int GetReadPayloadOffsetAndAdvance(int length)
|
||||
{
|
||||
if (length > _endIndex - _readIndex)
|
||||
if (length < 0 || length > _endIndex - _readIndex)
|
||||
{
|
||||
_readIndex = _endIndex;
|
||||
throw new InvalidOperationException($"Failed to read data with length of {length}");
|
||||
|
||||
@@ -5,8 +5,30 @@ using System.Text;
|
||||
|
||||
namespace AsarSharp.Utils
|
||||
{
|
||||
internal static class Extensions
|
||||
public static class Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Fills <paramref name="count"/> bytes. Stream.Read may legally return fewer than
|
||||
/// asked for; treating a short read as EOF corrupts header parsing and block hashes.
|
||||
/// Returns the bytes actually read, which is less than count only at end of stream.
|
||||
/// </summary>
|
||||
public static int ReadFull(this Stream stream, byte[] buffer, int offset, int count)
|
||||
{
|
||||
int total = 0;
|
||||
while (total < count)
|
||||
{
|
||||
int read = stream.Read(buffer, offset + total, count - total);
|
||||
if (read <= 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
total += read;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compute path relative to <paramref name="relativeTo"/>.
|
||||
/// Fast common-case (path is inside relativeTo): plain prefix-strip.
|
||||
@@ -170,19 +192,7 @@ namespace AsarSharp.Utils
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
return;
|
||||
|
||||
var process = new System.Diagnostics.Process
|
||||
{
|
||||
StartInfo = new System.Diagnostics.ProcessStartInfo
|
||||
{
|
||||
FileName = "chmod",
|
||||
Arguments = $"{permission} \"{filePath}\"",
|
||||
UseShellExecute = false,
|
||||
RedirectStandardOutput = true,
|
||||
CreateNoWindow = true
|
||||
}
|
||||
};
|
||||
process.Start();
|
||||
process.WaitForExit();
|
||||
RunTool("chmod", $"{permission} \"{filePath}\"");
|
||||
}
|
||||
|
||||
|
||||
@@ -190,32 +200,41 @@ namespace AsarSharp.Utils
|
||||
{
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
NativeMethods.CreateSymbolicLink(linkPath, linkTarget,
|
||||
bool success = NativeMethods.CreateSymbolicLink(linkPath, linkTarget,
|
||||
Directory.Exists(linkTarget)
|
||||
? NativeMethods.SymLinkFlag.Directory
|
||||
: NativeMethods.SymLinkFlag.File);
|
||||
if (!success)
|
||||
throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
|
||||
return;
|
||||
}
|
||||
|
||||
var process = new System.Diagnostics.Process
|
||||
{
|
||||
StartInfo = new System.Diagnostics.ProcessStartInfo
|
||||
{
|
||||
FileName = "ln",
|
||||
Arguments = $"-s \"{linkTarget}\" \"{linkPath}\"",
|
||||
UseShellExecute = false,
|
||||
RedirectStandardOutput = true,
|
||||
CreateNoWindow = true
|
||||
}
|
||||
};
|
||||
process.Start();
|
||||
process.WaitForExit();
|
||||
RunTool("ln", $"-s \"{linkTarget}\" \"{linkPath}\"");
|
||||
}
|
||||
|
||||
|
||||
public static bool IsWindowsPlatform()
|
||||
{
|
||||
return Environment.OSVersion.Platform == PlatformID.Win32NT;
|
||||
return RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
|
||||
}
|
||||
|
||||
private static void RunTool(string fileName, string arguments)
|
||||
{
|
||||
using (var process = new System.Diagnostics.Process
|
||||
{
|
||||
StartInfo = new System.Diagnostics.ProcessStartInfo
|
||||
{
|
||||
FileName = fileName,
|
||||
Arguments = arguments,
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true
|
||||
}
|
||||
})
|
||||
{
|
||||
process.Start();
|
||||
process.WaitForExit();
|
||||
if (process.ExitCode != 0)
|
||||
throw new InvalidOperationException($"Tool {fileName} failed with exit code {process.ExitCode}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user