mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2025-02-09 13:14:46 +01:00
Update the FsSystem namespace to use Fs.Path
This commit is contained in:
parent
6ba10074a3
commit
aad87ec845
@ -88,6 +88,7 @@ namespace LibHac.Fs.Common
|
||||
{
|
||||
_replacedChar = _buffer[1];
|
||||
_buffer[1] = 0;
|
||||
_position = 1;
|
||||
return _buffer;
|
||||
}
|
||||
|
||||
|
@ -538,12 +538,12 @@ namespace LibHac.Fs
|
||||
int parentBytesCopied = StringUtils.Copy(destBuffer, parent, parentLength + SeparatorLength);
|
||||
|
||||
// Make sure we copied the expected number of parent bytes.
|
||||
if (parentHasTrailingSlash)
|
||||
if (!parentHasTrailingSlash)
|
||||
{
|
||||
if (parentBytesCopied != parentLength + SeparatorLength)
|
||||
if (parentBytesCopied != parentLength)
|
||||
return ResultFs.UnexpectedInPathA.Log();
|
||||
}
|
||||
else if (parentBytesCopied != parentLength)
|
||||
else if (parentBytesCopied != parentLength + SeparatorLength)
|
||||
{
|
||||
return ResultFs.UnexpectedInPathA.Log();
|
||||
}
|
||||
@ -602,7 +602,7 @@ namespace LibHac.Fs
|
||||
if (_string[parentLength - 1] == DirectorySeparator || _string[parentLength - 1] == AltDirectorySeparator)
|
||||
parentLength--;
|
||||
|
||||
int childLength = StringUtils.GetLength(child);
|
||||
int childLength = StringUtils.GetLength(trimmedChild);
|
||||
|
||||
byte[] parentBuffer = null;
|
||||
try
|
||||
@ -630,15 +630,14 @@ namespace LibHac.Fs
|
||||
|
||||
if (childBytesCopied != childLength)
|
||||
return ResultFs.UnexpectedInPathA.Log();
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (parentBuffer is not null)
|
||||
ArrayPool<byte>.Shared.Return(parentBuffer);
|
||||
}
|
||||
|
||||
_isNormalized = false;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result AppendChild(in Path child)
|
||||
@ -737,7 +736,6 @@ namespace LibHac.Fs
|
||||
if (currentPos <= 0)
|
||||
return ResultFs.NotImplemented.Log();
|
||||
|
||||
_isNormalized = false;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
@ -809,7 +807,7 @@ namespace LibHac.Fs
|
||||
}
|
||||
|
||||
// Only a small number of format strings are used with these functions, so we can hard code them all easily.
|
||||
|
||||
|
||||
// /%s
|
||||
internal static Result SetUpFixedPathSingleEntry(ref Path path, Span<byte> pathBuffer,
|
||||
ReadOnlySpan<byte> entryName)
|
||||
|
@ -44,12 +44,6 @@ namespace LibHac.Fs.Fsa
|
||||
protected abstract Result DoRead(out long entriesRead, Span<DirectoryEntry> entryBuffer);
|
||||
protected abstract Result DoGetEntryCount(out long entryCount);
|
||||
|
||||
protected virtual void Dispose(bool disposing) { }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
public virtual void Dispose() { }
|
||||
}
|
||||
}
|
||||
|
@ -219,12 +219,6 @@ namespace LibHac.Fs.Fsa
|
||||
protected abstract Result DoOperateRange(Span<byte> outBuffer, OperationId operationId, long offset, long size,
|
||||
ReadOnlySpan<byte> inBuffer);
|
||||
|
||||
protected virtual void Dispose(bool disposing) { }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
public virtual void Dispose() { }
|
||||
}
|
||||
}
|
||||
|
@ -31,14 +31,11 @@ namespace LibHac.Fs.Impl
|
||||
BaseFile = baseFile.AddReference();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
public override void Dispose()
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
BaseFile?.Dispose();
|
||||
}
|
||||
BaseFile?.Dispose();
|
||||
|
||||
base.Dispose(disposing);
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
protected override Result DoRead(out long bytesRead, long offset, Span<byte> destination, in ReadOption option)
|
||||
@ -101,14 +98,11 @@ namespace LibHac.Fs.Impl
|
||||
BaseDirectory = baseDirectory.AddReference();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
public override void Dispose()
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
BaseDirectory?.Dispose();
|
||||
}
|
||||
BaseDirectory?.Dispose();
|
||||
|
||||
base.Dispose(disposing);
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
protected override Result DoRead(out long entriesRead, Span<DirectoryEntry> entryBuffer)
|
||||
|
@ -7,7 +7,7 @@ using LibHac.FsSystem;
|
||||
using LibHac.Sf;
|
||||
using IFileSystem = LibHac.Fs.Fsa.IFileSystem;
|
||||
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
|
||||
using PathNormalizer = LibHac.FsSrv.Impl.PathNormalizer;
|
||||
using Path = LibHac.Fs.Path;
|
||||
|
||||
namespace LibHac.FsSrv
|
||||
{
|
||||
@ -117,7 +117,7 @@ namespace LibHac.FsSrv
|
||||
using var scopedContext = new ScopedStorageLayoutTypeSetter(storageFlag);
|
||||
|
||||
// Normalize the path
|
||||
var pathNormalized = new Fs.Path();
|
||||
var pathNormalized = new Path();
|
||||
rc = pathNormalized.Initialize(rootPath.Str);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
@ -135,7 +135,7 @@ namespace LibHac.FsSrv
|
||||
rc = _serviceImpl.OpenBisFileSystem(out baseFileSystem, partitionId, false);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = Impl.Utility.CreateSubDirectoryFileSystem(out fileSystem, ref baseFileSystem, in pathNormalized);
|
||||
rc = Utility.CreateSubDirectoryFileSystem(out fileSystem, ref baseFileSystem, in pathNormalized);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
// Add all the file system wrappers
|
||||
|
@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs;
|
||||
using LibHac.FsSrv.FsCreator;
|
||||
using LibHac.FsSrv.Impl;
|
||||
|
@ -77,6 +77,11 @@ namespace LibHac.FsSrv.FsCreator
|
||||
Result rc = bisRootPath.Initialize(GetPartitionPath(partitionId).ToU8String());
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
var pathFlags = new PathFlags();
|
||||
pathFlags.AllowEmptyPath();
|
||||
rc = bisRootPath.Normalize(pathFlags);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
ReferenceCountedDisposable<IFileSystem> partitionFileSystem = null;
|
||||
ReferenceCountedDisposable<IFileSystem> sharedRootFs = null;
|
||||
try
|
||||
|
@ -83,7 +83,7 @@ namespace LibHac.FsSrv.FsCreator
|
||||
try
|
||||
{
|
||||
tempFs = _rootFileSystem.AddReference();
|
||||
rc = Utility.CreateSubDirectoryFileSystem(out _sdCardFileSystem, ref tempFs, in sdCardPath);
|
||||
rc = Utility.WrapSubDirectory(out _sdCardFileSystem, ref tempFs, in sdCardPath, true);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
outFileSystem = _sdCardFileSystem.AddReference();
|
||||
|
@ -1,13 +1,10 @@
|
||||
using System;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs.Fsa;
|
||||
|
||||
namespace LibHac.FsSrv.FsCreator
|
||||
{
|
||||
public interface ITargetManagerFileSystemCreator
|
||||
{
|
||||
// Todo: Remove raw IFilesystem function
|
||||
Result Create(out IFileSystem fileSystem, bool openCaseSensitive);
|
||||
Result Create(out ReferenceCountedDisposable<IFileSystem> fileSystem, in Path rootPath, bool openCaseSensitive, bool ensureRootPathExists, Result pathNotFoundResult);
|
||||
Result NormalizeCaseOfPath(out bool isSupported, ref Path path);
|
||||
}
|
||||
|
@ -6,11 +6,6 @@ namespace LibHac.FsSrv.FsCreator
|
||||
{
|
||||
public class TargetManagerFileSystemCreator : ITargetManagerFileSystemCreator
|
||||
{
|
||||
public Result Create(out IFileSystem fileSystem, bool openCaseSensitive)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Result Create(out ReferenceCountedDisposable<IFileSystem> fileSystem, in Path rootPath, bool openCaseSensitive, bool ensureRootPathExists, Result pathNotFoundResult)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
|
@ -15,15 +15,12 @@ namespace LibHac.FsSrv.Impl
|
||||
BaseFile = baseFile;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
public override void Dispose()
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
BaseFile?.Dispose();
|
||||
BaseFile = null;
|
||||
}
|
||||
BaseFile?.Dispose();
|
||||
BaseFile = null;
|
||||
|
||||
base.Dispose(disposing);
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
protected override Result DoRead(out long bytesRead, long offset, Span<byte> destination, in ReadOption option)
|
||||
@ -70,15 +67,12 @@ namespace LibHac.FsSrv.Impl
|
||||
BaseDirectory = baseDirectory;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
public override void Dispose()
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
BaseDirectory?.Dispose();
|
||||
BaseDirectory = null;
|
||||
}
|
||||
BaseDirectory?.Dispose();
|
||||
BaseDirectory = null;
|
||||
|
||||
base.Dispose(disposing);
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
protected override Result DoRead(out long entriesRead, Span<DirectoryEntry> entryBuffer)
|
||||
|
@ -8,13 +8,11 @@ using LibHac.FsSystem;
|
||||
using LibHac.Lr;
|
||||
using LibHac.Ncm;
|
||||
using LibHac.Spl;
|
||||
using LibHac.Util;
|
||||
using IFileSystem = LibHac.Fs.Fsa.IFileSystem;
|
||||
using IStorage = LibHac.Fs.IStorage;
|
||||
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
|
||||
using IStorageSf = LibHac.FsSrv.Sf.IStorage;
|
||||
using Path = LibHac.Fs.Path;
|
||||
using PathNormalizer = LibHac.FsSrv.Impl.PathNormalizer;
|
||||
using Utility = LibHac.FsSrv.Impl.Utility;
|
||||
|
||||
namespace LibHac.FsSrv
|
||||
@ -96,7 +94,7 @@ namespace LibHac.FsSrv
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
// Try to find the path to the original version of the file system
|
||||
var originalPath = new Fs.Path();
|
||||
var originalPath = new Path();
|
||||
Result originalResult = ServiceImpl.ResolveApplicationHtmlDocumentPath(out bool isDirectory,
|
||||
ref originalPath, new Ncm.ApplicationId(programId.Value), ownerProgramInfo.StorageId);
|
||||
|
||||
@ -105,7 +103,7 @@ namespace LibHac.FsSrv
|
||||
return originalResult;
|
||||
|
||||
// Try to find the path to the patch file system
|
||||
var patchPath = new Fs.Path();
|
||||
var patchPath = new Path();
|
||||
Result patchResult = ServiceImpl.ResolveRegisteredHtmlDocumentPath(ref patchPath, programId.Value);
|
||||
|
||||
ReferenceCountedDisposable<IFileSystem> tempFileSystem = null;
|
||||
@ -128,11 +126,11 @@ namespace LibHac.FsSrv
|
||||
if (patchResult.IsFailure())
|
||||
return patchResult;
|
||||
|
||||
var emptyPath = new Fs.Path();
|
||||
var emptyPath = new Path();
|
||||
rc = emptyPath.InitializeAsEmpty();
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
ref Fs.Path originalNcaPath = ref originalResult.IsSuccess() ? ref originalPath : ref emptyPath;
|
||||
ref Path originalNcaPath = ref originalResult.IsSuccess() ? ref originalPath : ref emptyPath;
|
||||
|
||||
// Open the file system using both the original and patch versions
|
||||
rc = ServiceImpl.OpenFileSystemWithPatch(out tempFileSystem, in originalNcaPath, in patchPath,
|
||||
@ -235,7 +233,7 @@ namespace LibHac.FsSrv
|
||||
|
||||
bool canMountSystemDataPrivate = ac.GetAccessibilityFor(AccessibilityType.MountSystemDataPrivate).CanRead;
|
||||
|
||||
var pathNormalized = new Fs.Path();
|
||||
var pathNormalized = new Path();
|
||||
rc = pathNormalized.InitializeWithReplaceUnc(path.Str);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
|
@ -17,7 +17,6 @@ using IFile = LibHac.Fs.Fsa.IFile;
|
||||
using IFileSf = LibHac.FsSrv.Sf.IFile;
|
||||
using Path = LibHac.Fs.Path;
|
||||
using SaveData = LibHac.Fs.SaveData;
|
||||
using Utility = LibHac.FsSystem.Utility;
|
||||
using static LibHac.Fs.StringTraits;
|
||||
|
||||
namespace LibHac.FsSrv
|
||||
@ -1801,7 +1800,7 @@ namespace LibHac.FsSrv
|
||||
saveDataId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
commitId = Impl.Utility.ConvertZeroCommitId(in extraData);
|
||||
commitId = Utility.ConvertZeroCommitId(in extraData);
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
@ -2228,7 +2227,7 @@ namespace LibHac.FsSrv
|
||||
{
|
||||
saveService = _selfReference.AddReference();
|
||||
|
||||
Result rc = Utility.MakeUniqueLockWithPin(out uniqueLock, _openEntryCountSemaphore,
|
||||
Result rc = Utility12.MakeUniqueLockWithPin(out uniqueLock, _openEntryCountSemaphore,
|
||||
ref saveService);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
@ -2252,7 +2251,7 @@ namespace LibHac.FsSrv
|
||||
{
|
||||
saveService = _selfReference.AddReference();
|
||||
|
||||
Result rc = Utility.MakeUniqueLockWithPin(out uniqueLock, _saveDataMountCountSemaphore,
|
||||
Result rc = Utility12.MakeUniqueLockWithPin(out uniqueLock, _saveDataMountCountSemaphore,
|
||||
ref saveService);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
|
@ -218,7 +218,7 @@ namespace LibHac.FsSrv
|
||||
UnsafeHelpers.SkipParamInit(out fileSystem);
|
||||
|
||||
// Hack around error CS8350.
|
||||
const int bufferLength = 0xF;
|
||||
const int bufferLength = 0x1B;
|
||||
Span<byte> buffer = stackalloc byte[bufferLength];
|
||||
ref byte bufferRef = ref MemoryMarshal.GetReference(buffer);
|
||||
Span<byte> saveDataMetaIdDirectoryNameBuffer = MemoryMarshal.CreateSpan(ref bufferRef, bufferLength);
|
||||
|
@ -122,14 +122,13 @@ namespace LibHac.FsSystem
|
||||
return BaseStorage.SetSize(Alignment.AlignUp(size, 0x10));
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
public override void Dispose()
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
BaseStorage.Flush();
|
||||
BaseStorage.Dispose();
|
||||
BaseFile?.Dispose();
|
||||
}
|
||||
BaseStorage.Flush();
|
||||
BaseStorage.Dispose();
|
||||
BaseFile?.Dispose();
|
||||
|
||||
base.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -57,16 +57,16 @@ namespace LibHac.FsSystem
|
||||
/// <param name="options">Flags to control how the file is created.
|
||||
/// Should usually be <see cref="CreateFileOptions.None"/></param>
|
||||
/// <param name="key">The 256-bit key containing a 128-bit data key followed by a 128-bit tweak key.</param>
|
||||
public Result CreateFile(U8Span path, long size, CreateFileOptions options, byte[] key)
|
||||
public Result CreateFile(in Path path, long size, CreateFileOptions options, byte[] key)
|
||||
{
|
||||
long containerSize = AesXtsFile.HeaderLength + Alignment.AlignUp(size, 0x10);
|
||||
|
||||
Result rc = BaseFileSystem.CreateFile(path, containerSize, options);
|
||||
Result rc = BaseFileSystem.CreateFile(in path, containerSize, options);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
var header = new AesXtsFileHeader(key, size, path.ToString(), KekSource, ValidationKey);
|
||||
|
||||
rc = BaseFileSystem.OpenFile(out IFile baseFile, path, OpenMode.Write);
|
||||
rc = BaseFileSystem.OpenFile(out IFile baseFile, in path, OpenMode.Write);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
using (baseFile)
|
||||
@ -105,7 +105,7 @@ namespace LibHac.FsSystem
|
||||
Result rc = BaseFileSystem.OpenDirectory(out IDirectory baseDir, path, mode);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
directory = new AesXtsDirectory(BaseFileSystem, baseDir, path.ToU8String(), mode);
|
||||
directory = new AesXtsDirectory(BaseFileSystem, baseDir, new U8String(path.GetString().ToArray()), mode);
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
@ -116,7 +116,8 @@ namespace LibHac.FsSystem
|
||||
Result rc = BaseFileSystem.OpenFile(out IFile baseFile, path, mode | OpenMode.Read);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
var xtsFile = new AesXtsFile(mode, baseFile, path.ToU8String(), KekSource, ValidationKey, BlockSize);
|
||||
var xtsFile = new AesXtsFile(mode, baseFile, new U8String(path.GetString().ToArray()), KekSource,
|
||||
ValidationKey, BlockSize);
|
||||
|
||||
file = xtsFile;
|
||||
return Result.Success;
|
||||
|
@ -46,19 +46,18 @@ namespace LibHac.FsSystem
|
||||
_path = new Path.Stored();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
public override void Dispose()
|
||||
{
|
||||
_path.Dispose();
|
||||
|
||||
if (disposing)
|
||||
foreach (IFile file in _files)
|
||||
{
|
||||
foreach (IFile file in _files)
|
||||
{
|
||||
file?.Dispose();
|
||||
}
|
||||
|
||||
_files.Clear();
|
||||
file?.Dispose();
|
||||
}
|
||||
|
||||
_files.Clear();
|
||||
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
public Result Initialize(in Path path)
|
||||
@ -391,15 +390,12 @@ namespace LibHac.FsSystem
|
||||
_concatenationFileSystem = concatFileSystem;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
public override void Dispose()
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_path.Dispose();
|
||||
_baseDirectory.Dispose();
|
||||
}
|
||||
_path.Dispose();
|
||||
_baseDirectory.Dispose();
|
||||
|
||||
base.Dispose(disposing);
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
public Result Initialize(in Path path)
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,86 +0,0 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.Util;
|
||||
|
||||
namespace LibHac.FsSystem
|
||||
{
|
||||
public static class DirectoryUtils
|
||||
{
|
||||
public delegate Result Blah(ReadOnlySpan<byte> path, ref DirectoryEntry entry);
|
||||
|
||||
public static Result IterateDirectoryRecursivelyInternal(IFileSystem fs, Span<byte> workPath,
|
||||
ref DirectoryEntry entry, Blah onEnterDir, Blah onExitDir, Blah onFile)
|
||||
{
|
||||
Result rc = fs.OpenDirectory(out IDirectory _, new U8Span(workPath), OpenDirectoryMode.All);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
onFile(workPath, ref entry);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public static Result IterateDirectoryRecursively(IFileSystem fs, ReadOnlySpan<byte> path, Blah onEnterDir, Blah onExitDir, Blah onFile)
|
||||
{
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public static Result CopyDirectoryRecursively(IFileSystem sourceFs, IFileSystem destFs, string sourcePath,
|
||||
string destPath)
|
||||
{
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public static Result CopyFile(IFileSystem destFs, IFileSystem sourceFs, ReadOnlySpan<byte> destParentPath,
|
||||
ReadOnlySpan<byte> sourcePath, ref DirectoryEntry dirEntry, Span<byte> copyBuffer)
|
||||
{
|
||||
IFile srcFile = null;
|
||||
IFile dstFile = null;
|
||||
|
||||
try
|
||||
{
|
||||
Result rc = sourceFs.OpenFile(out srcFile, new U8Span(sourcePath), OpenMode.Read);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
Unsafe.SkipInit(out FsPath dstPath);
|
||||
dstPath.Str[0] = 0;
|
||||
int dstPathLen = StringUtils.Concat(dstPath.Str, destParentPath);
|
||||
dstPathLen = StringUtils.Concat(dstPath.Str, dirEntry.Name, dstPathLen);
|
||||
|
||||
if (dstPathLen > FsPath.MaxLength)
|
||||
{
|
||||
throw new ArgumentException();
|
||||
}
|
||||
|
||||
rc = destFs.CreateFile(dstPath, dirEntry.Size, CreateFileOptions.None);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = destFs.OpenFile(out dstFile, dstPath, OpenMode.Write);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
long fileSize = dirEntry.Size;
|
||||
long offset = 0;
|
||||
|
||||
while (offset < fileSize)
|
||||
{
|
||||
rc = srcFile.Read(out long bytesRead, offset, copyBuffer, ReadOption.None);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = dstFile.Write(offset, copyBuffer.Slice(0, (int)bytesRead), WriteOption.None);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
offset += bytesRead;
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
finally
|
||||
{
|
||||
srcFile?.Dispose();
|
||||
dstFile?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -7,6 +7,7 @@ using LibHac.Common;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.Util;
|
||||
using Path = LibHac.Fs.Path;
|
||||
|
||||
namespace LibHac.FsSystem
|
||||
{
|
||||
@ -15,38 +16,119 @@ namespace LibHac.FsSystem
|
||||
public static Result CopyDirectory(this IFileSystem sourceFs, IFileSystem destFs, string sourcePath, string destPath,
|
||||
IProgressReport logger = null, CreateFileOptions options = CreateFileOptions.None)
|
||||
{
|
||||
Result rc;
|
||||
const int bufferSize = 0x100000;
|
||||
|
||||
foreach (DirectoryEntryEx entry in sourceFs.EnumerateEntries(sourcePath, "*", SearchOptions.Default))
|
||||
var directoryEntryBuffer = new DirectoryEntry();
|
||||
|
||||
var sourcePathNormalized = new Path();
|
||||
Result rc = InitializeFromString(ref sourcePathNormalized, sourcePath);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
var destPathNormalized = new Path();
|
||||
rc = InitializeFromString(ref destPathNormalized, destPath);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
byte[] workBuffer = ArrayPool<byte>.Shared.Rent(bufferSize);
|
||||
try
|
||||
{
|
||||
string subSrcPath = PathTools.Normalize(PathTools.Combine(sourcePath, entry.Name));
|
||||
string subDstPath = PathTools.Normalize(PathTools.Combine(destPath, entry.Name));
|
||||
return CopyDirectoryRecursively(destFs, sourceFs, in destPathNormalized, in sourcePathNormalized,
|
||||
ref directoryEntryBuffer, workBuffer, logger, options);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(workBuffer);
|
||||
logger?.SetTotal(0);
|
||||
}
|
||||
}
|
||||
|
||||
if (entry.Type == DirectoryEntryType.Directory)
|
||||
public static Result CopyDirectoryRecursively(IFileSystem destinationFileSystem, IFileSystem sourceFileSystem,
|
||||
in Path destinationPath, in Path sourcePath, ref DirectoryEntry dirEntry, Span<byte> workBuffer,
|
||||
IProgressReport logger = null, CreateFileOptions option = CreateFileOptions.None)
|
||||
{
|
||||
static Result OnEnterDir(in Path path, in DirectoryEntry entry,
|
||||
ref Utility12.FsIterationTaskClosure closure)
|
||||
{
|
||||
Result rc = closure.DestinationPathBuffer.AppendChild(entry.Name);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return closure.SourceFileSystem.CreateDirectory(in closure.DestinationPathBuffer);
|
||||
}
|
||||
|
||||
static Result OnExitDir(in Path path, in DirectoryEntry entry, ref Utility12.FsIterationTaskClosure closure)
|
||||
{
|
||||
return closure.DestinationPathBuffer.RemoveChild();
|
||||
}
|
||||
|
||||
Result OnFile(in Path path, in DirectoryEntry entry, ref Utility12.FsIterationTaskClosure closure)
|
||||
{
|
||||
logger?.LogMessage(path.ToString());
|
||||
|
||||
Result result = closure.DestinationPathBuffer.AppendChild(entry.Name);
|
||||
if (result.IsFailure()) return result;
|
||||
|
||||
result = CopyFile(closure.DestFileSystem, closure.SourceFileSystem, in closure.DestinationPathBuffer,
|
||||
in path, closure.Buffer, logger, option);
|
||||
if (result.IsFailure()) return result;
|
||||
|
||||
return closure.DestinationPathBuffer.RemoveChild();
|
||||
}
|
||||
|
||||
var taskClosure = new Utility12.FsIterationTaskClosure();
|
||||
taskClosure.Buffer = workBuffer;
|
||||
taskClosure.SourceFileSystem = sourceFileSystem;
|
||||
taskClosure.DestFileSystem = destinationFileSystem;
|
||||
|
||||
Result rc = taskClosure.DestinationPathBuffer.Initialize(destinationPath);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = Utility12.IterateDirectoryRecursively(sourceFileSystem, in sourcePath, ref dirEntry, OnEnterDir,
|
||||
OnExitDir, OnFile, ref taskClosure);
|
||||
|
||||
taskClosure.DestinationPathBuffer.Dispose();
|
||||
return rc;
|
||||
}
|
||||
|
||||
public static Result CopyFile(IFileSystem destFileSystem, IFileSystem sourceFileSystem, in Path destPath,
|
||||
in Path sourcePath, Span<byte> workBuffer, IProgressReport logger = null,
|
||||
CreateFileOptions option = CreateFileOptions.None)
|
||||
{
|
||||
logger?.LogMessage(sourcePath.ToString());
|
||||
|
||||
// Open source file.
|
||||
Result rc = sourceFileSystem.OpenFile(out IFile sourceFile, sourcePath, OpenMode.Read);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
using (sourceFile)
|
||||
{
|
||||
rc = sourceFile.GetSize(out long fileSize);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = CreateOrOverwriteFile(destFileSystem, in destPath, fileSize, option);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = destFileSystem.OpenFile(out IFile destFile, in destPath, OpenMode.Write);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
using (destFile)
|
||||
{
|
||||
destFs.EnsureDirectoryExists(subDstPath);
|
||||
// Read/Write file in work buffer sized chunks.
|
||||
long remaining = fileSize;
|
||||
long offset = 0;
|
||||
|
||||
rc = sourceFs.CopyDirectory(destFs, subSrcPath, subDstPath, logger, options);
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
logger?.SetTotal(fileSize);
|
||||
|
||||
if (entry.Type == DirectoryEntryType.File)
|
||||
{
|
||||
destFs.CreateOrOverwriteFile(subDstPath, entry.Size, options);
|
||||
|
||||
rc = sourceFs.OpenFile(out IFile srcFile, subSrcPath.ToU8Span(), OpenMode.Read);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
using (srcFile)
|
||||
while (remaining > 0)
|
||||
{
|
||||
rc = destFs.OpenFile(out IFile dstFile, subDstPath.ToU8Span(), OpenMode.Write | OpenMode.AllowAppend);
|
||||
rc = sourceFile.Read(out long bytesRead, offset, workBuffer, ReadOption.None);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
using (dstFile)
|
||||
{
|
||||
logger?.LogMessage(subSrcPath);
|
||||
srcFile.CopyTo(dstFile, logger);
|
||||
}
|
||||
rc = destFile.Write(offset, workBuffer.Slice(0, (int)bytesRead), WriteOption.None);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
remaining -= bytesRead;
|
||||
offset += bytesRead;
|
||||
|
||||
logger?.ReportAdd(bytesRead);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -81,9 +163,10 @@ namespace LibHac.FsSystem
|
||||
bool ignoreCase = searchOptions.HasFlag(SearchOptions.CaseInsensitive);
|
||||
bool recurse = searchOptions.HasFlag(SearchOptions.RecurseSubdirectories);
|
||||
|
||||
IFileSystem fs = fileSystem;
|
||||
var pathNormalized = new Path();
|
||||
InitializeFromString(ref pathNormalized, path).ThrowIfFailure();
|
||||
|
||||
fileSystem.OpenDirectory(out IDirectory directory, path.ToU8Span(), OpenDirectoryMode.All).ThrowIfFailure();
|
||||
fileSystem.OpenDirectory(out IDirectory directory, in pathNormalized, OpenDirectoryMode.All).ThrowIfFailure();
|
||||
|
||||
while (true)
|
||||
{
|
||||
@ -102,7 +185,7 @@ namespace LibHac.FsSystem
|
||||
if (entry.Type != DirectoryEntryType.Directory || !recurse) continue;
|
||||
|
||||
IEnumerable<DirectoryEntryEx> subEntries =
|
||||
fs.EnumerateEntries(PathTools.Combine(path, entry.Name), searchPattern,
|
||||
fileSystem.EnumerateEntries(PathTools.Combine(path, entry.Name), searchPattern,
|
||||
searchOptions);
|
||||
|
||||
foreach (DirectoryEntryEx subEntry in subEntries)
|
||||
@ -202,7 +285,10 @@ namespace LibHac.FsSystem
|
||||
|
||||
public static void SetConcatenationFileAttribute(this IFileSystem fs, string path)
|
||||
{
|
||||
fs.QueryEntry(Span<byte>.Empty, Span<byte>.Empty, QueryId.SetConcatenationFileAttribute, path.ToU8Span());
|
||||
var pathNormalized = new Path();
|
||||
InitializeFromString(ref pathNormalized, path).ThrowIfFailure();
|
||||
|
||||
fs.QueryEntry(Span<byte>.Empty, Span<byte>.Empty, QueryId.SetConcatenationFileAttribute, in pathNormalized);
|
||||
}
|
||||
|
||||
public static void CleanDirectoryRecursivelyGeneric(IFileSystem fileSystem, string path)
|
||||
@ -213,14 +299,17 @@ namespace LibHac.FsSystem
|
||||
{
|
||||
string subPath = PathTools.Combine(path, entry.Name);
|
||||
|
||||
var subPathNormalized = new Path();
|
||||
InitializeFromString(ref subPathNormalized, subPath).ThrowIfFailure();
|
||||
|
||||
if (entry.Type == DirectoryEntryType.Directory)
|
||||
{
|
||||
CleanDirectoryRecursivelyGeneric(fileSystem, subPath);
|
||||
fs.DeleteDirectory(subPath.ToU8Span());
|
||||
fs.DeleteDirectory(in subPathNormalized);
|
||||
}
|
||||
else if (entry.Type == DirectoryEntryType.File)
|
||||
{
|
||||
fs.DeleteFile(subPath.ToU8Span());
|
||||
fs.DeleteFile(in subPathNormalized);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -251,55 +340,46 @@ namespace LibHac.FsSystem
|
||||
|
||||
public static Result EnsureDirectoryExists(this IFileSystem fs, string path)
|
||||
{
|
||||
path = PathTools.Normalize(path);
|
||||
if (fs.DirectoryExists(path)) return Result.Success;
|
||||
var pathNormalized = new Path();
|
||||
Result rc = InitializeFromString(ref pathNormalized, path);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
// Find the first subdirectory in the chain that doesn't exist
|
||||
int i;
|
||||
for (i = path.Length - 1; i > 0; i--)
|
||||
{
|
||||
if (path[i] == '/')
|
||||
{
|
||||
string subPath = path.Substring(0, i);
|
||||
|
||||
if (fs.DirectoryExists(subPath))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// path[i] will be a '/', so skip that character
|
||||
i++;
|
||||
|
||||
// loop until `path.Length - 1` so CreateDirectory won't be called multiple
|
||||
// times on path if the last character in the path is a '/'
|
||||
for (; i < path.Length - 1; i++)
|
||||
{
|
||||
if (path[i] == '/')
|
||||
{
|
||||
string subPath = path.Substring(0, i);
|
||||
|
||||
Result rc = fs.CreateDirectory(subPath.ToU8Span());
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
}
|
||||
|
||||
return fs.CreateDirectory(path.ToU8Span());
|
||||
return Utility12.EnsureDirectory(fs, in pathNormalized);
|
||||
}
|
||||
|
||||
public static void CreateOrOverwriteFile(this IFileSystem fs, string path, long size)
|
||||
public static Result CreateOrOverwriteFile(IFileSystem fileSystem, in Path path, long size,
|
||||
CreateFileOptions option = CreateFileOptions.None)
|
||||
{
|
||||
fs.CreateOrOverwriteFile(path, size, CreateFileOptions.None);
|
||||
Result rc = fileSystem.CreateFile(in path, size, option);
|
||||
|
||||
if (rc.IsFailure())
|
||||
{
|
||||
if (!ResultFs.PathAlreadyExists.Includes(rc))
|
||||
return rc;
|
||||
|
||||
rc = fileSystem.DeleteFile(in path);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = fileSystem.CreateFile(in path, size, option);
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public static void CreateOrOverwriteFile(this IFileSystem fs, string path, long size, CreateFileOptions options)
|
||||
private static Result InitializeFromString(ref Path outPath, string path)
|
||||
{
|
||||
path = PathTools.Normalize(path);
|
||||
ReadOnlySpan<byte> utf8Path = StringUtils.StringToUtf8(path);
|
||||
|
||||
if (fs.FileExists(path)) fs.DeleteFile(path.ToU8Span());
|
||||
Result rc = outPath.Initialize(utf8Path);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
fs.CreateFile(path.ToU8Span(), size, CreateFileOptions.None);
|
||||
var pathFlags = new PathFlags();
|
||||
pathFlags.AllowEmptyPath();
|
||||
outPath.Normalize(pathFlags);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
|
||||
|
31
src/LibHac/FsSystem/IUniqueLock.cs
Normal file
31
src/LibHac/FsSystem/IUniqueLock.cs
Normal file
@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using LibHac.Common;
|
||||
using LibHac.Os;
|
||||
|
||||
namespace LibHac.FsSystem
|
||||
{
|
||||
public interface IUniqueLock : IDisposable { }
|
||||
|
||||
public class UniqueLockWithPin<T> : IUniqueLock where T : class, IDisposable
|
||||
{
|
||||
private UniqueLock<SemaphoreAdapter> _semaphore;
|
||||
private ReferenceCountedDisposable<T> _pinnedObject;
|
||||
|
||||
public UniqueLockWithPin(ref UniqueLock<SemaphoreAdapter> semaphore, ref ReferenceCountedDisposable<T> pinnedObject)
|
||||
{
|
||||
Shared.Move(out _semaphore, ref semaphore);
|
||||
Shared.Move(out _pinnedObject, ref pinnedObject);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_pinnedObject != null)
|
||||
{
|
||||
_semaphore.Dispose();
|
||||
_pinnedObject.Dispose();
|
||||
|
||||
_pinnedObject = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -48,7 +48,7 @@ namespace LibHac.FsSystem
|
||||
|
||||
foreach (IFileSystem fs in Sources)
|
||||
{
|
||||
Result rc = fs.GetEntryType(out DirectoryEntryType entryType, path);
|
||||
Result rc = fs.GetEntryType(out DirectoryEntryType entryType, in path);
|
||||
|
||||
if (rc.IsSuccess())
|
||||
{
|
||||
@ -82,8 +82,8 @@ namespace LibHac.FsSystem
|
||||
|
||||
if (!(multipleSources is null))
|
||||
{
|
||||
var dir = new MergedDirectory(multipleSources, path, mode);
|
||||
Result rc = dir.Initialize();
|
||||
var dir = new MergedDirectory(multipleSources, mode);
|
||||
Result rc = dir.Initialize(in path);
|
||||
|
||||
if (rc.IsSuccess())
|
||||
{
|
||||
@ -207,25 +207,27 @@ namespace LibHac.FsSystem
|
||||
// Needed to open new directories for GetEntryCount
|
||||
private List<IFileSystem> SourceFileSystems { get; }
|
||||
private List<IDirectory> SourceDirs { get; }
|
||||
private U8String Path { get; }
|
||||
private Path.Stored _path;
|
||||
private OpenDirectoryMode Mode { get; }
|
||||
|
||||
// todo: Efficient way to remove duplicates
|
||||
private HashSet<string> Names { get; } = new HashSet<string>();
|
||||
|
||||
public MergedDirectory(List<IFileSystem> sourceFileSystems, U8Span path, OpenDirectoryMode mode)
|
||||
public MergedDirectory(List<IFileSystem> sourceFileSystems, OpenDirectoryMode mode)
|
||||
{
|
||||
SourceFileSystems = sourceFileSystems;
|
||||
SourceDirs = new List<IDirectory>(sourceFileSystems.Count);
|
||||
Path = path.ToU8String();
|
||||
Mode = mode;
|
||||
}
|
||||
|
||||
public Result Initialize()
|
||||
public Result Initialize(in Path path)
|
||||
{
|
||||
Result rc = _path.Initialize(in path);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
foreach (IFileSystem fs in SourceFileSystems)
|
||||
{
|
||||
Result rc = fs.OpenDirectory(out IDirectory dir, Path, Mode);
|
||||
rc = fs.OpenDirectory(out IDirectory dir, in path, Mode);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
SourceDirs.Add(dir);
|
||||
@ -268,10 +270,12 @@ namespace LibHac.FsSystem
|
||||
// todo: Efficient way to remove duplicates
|
||||
var names = new HashSet<string>();
|
||||
|
||||
Path path = _path.GetPath();
|
||||
|
||||
// Open new directories for each source because we need to remove duplicate entries
|
||||
foreach (IFileSystem fs in SourceFileSystems)
|
||||
{
|
||||
Result rc = fs.OpenDirectory(out IDirectory dir, Path, Mode);
|
||||
Result rc = fs.OpenDirectory(out IDirectory dir, in path, Mode);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
long entriesRead;
|
||||
|
@ -92,14 +92,12 @@ namespace LibHac.FsSystem
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
public override void Dispose()
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
File?.Dispose();
|
||||
}
|
||||
|
||||
File?.Dispose();
|
||||
Stream?.Dispose();
|
||||
|
||||
base.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,6 @@
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text.Unicode;
|
||||
using System.Threading;
|
||||
using LibHac.Common;
|
||||
@ -33,7 +32,8 @@ namespace LibHac.FsSystem
|
||||
CaseSensitive
|
||||
}
|
||||
|
||||
private string _rootPath;
|
||||
private Path.Stored _rootPath;
|
||||
private string _rootPathUtf16;
|
||||
private readonly FileSystemClient _fsClient;
|
||||
private PathMode _mode;
|
||||
private readonly bool _useUnixTime;
|
||||
@ -57,17 +57,9 @@ namespace LibHac.FsSystem
|
||||
/// <param name="rootPath">The path that will be the root of the <see cref="LocalFileSystem"/>.</param>
|
||||
public LocalFileSystem(string rootPath)
|
||||
{
|
||||
_rootPath = System.IO.Path.GetFullPath(rootPath);
|
||||
|
||||
if (!Directory.Exists(_rootPath))
|
||||
{
|
||||
if (File.Exists(_rootPath))
|
||||
{
|
||||
throw new DirectoryNotFoundException($"The specified path is a file. ({rootPath})");
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(_rootPath);
|
||||
}
|
||||
Result rc = Initialize(rootPath, PathMode.DefaultCaseSensitivity, true);
|
||||
if (rc.IsFailure())
|
||||
throw new HorizonResultException(rc, "Error creating LocalFileSystem.");
|
||||
}
|
||||
|
||||
public static Result Create(out LocalFileSystem fileSystem, string rootPath,
|
||||
@ -85,6 +77,8 @@ namespace LibHac.FsSystem
|
||||
|
||||
public Result Initialize(string rootPath, PathMode pathMode, bool ensurePathExists)
|
||||
{
|
||||
Result rc;
|
||||
|
||||
if (rootPath == null)
|
||||
return ResultFs.NullptrArgument.Log();
|
||||
|
||||
@ -93,13 +87,21 @@ namespace LibHac.FsSystem
|
||||
// If the root path is empty, we interpret any incoming paths as rooted paths.
|
||||
if (rootPath == string.Empty)
|
||||
{
|
||||
_rootPath = rootPath;
|
||||
var path = new Path();
|
||||
rc = path.InitializeAsEmpty();
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = _rootPath.Initialize(in path);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
string rootPathNormalized;
|
||||
|
||||
try
|
||||
{
|
||||
_rootPath = System.IO.Path.GetFullPath(rootPath);
|
||||
rootPathNormalized = System.IO.Path.GetFullPath(rootPath);
|
||||
}
|
||||
catch (PathTooLongException)
|
||||
{
|
||||
@ -110,14 +112,14 @@ namespace LibHac.FsSystem
|
||||
return ResultFs.InvalidCharacter.Log();
|
||||
}
|
||||
|
||||
if (!Directory.Exists(_rootPath))
|
||||
if (!Directory.Exists(rootPathNormalized))
|
||||
{
|
||||
if (!ensurePathExists || File.Exists(_rootPath))
|
||||
if (!ensurePathExists || File.Exists(rootPathNormalized))
|
||||
return ResultFs.PathNotFound.Log();
|
||||
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(_rootPath);
|
||||
Directory.CreateDirectory(rootPathNormalized);
|
||||
}
|
||||
catch (Exception ex) when (ex.HResult < 0)
|
||||
{
|
||||
@ -125,53 +127,78 @@ namespace LibHac.FsSystem
|
||||
}
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
ReadOnlySpan<byte> utf8Path = StringUtils.StringToUtf8(rootPathNormalized);
|
||||
var pathNormalized = new Path();
|
||||
|
||||
private Result ResolveFullPath(out string fullPath, U8Span path, bool checkCaseSensitivity)
|
||||
{
|
||||
UnsafeHelpers.SkipParamInit(out fullPath);
|
||||
|
||||
Unsafe.SkipInit(out FsPath normalizedPath);
|
||||
|
||||
Result rc = PathNormalizer.Normalize(normalizedPath.Str, out _, path, false, false);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
fullPath = PathTools.Combine(_rootPath, normalizedPath.ToString());
|
||||
|
||||
if (_mode == PathMode.CaseSensitive && checkCaseSensitivity)
|
||||
if (utf8Path.At(0) == DirectorySeparator && utf8Path.At(1) != DirectorySeparator)
|
||||
{
|
||||
rc = CheckPathCaseSensitively(fullPath);
|
||||
rc = pathNormalized.Initialize(utf8Path.Slice(1));
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = pathNormalized.InitializeWithReplaceUnc(utf8Path);
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
|
||||
var flags = new PathFlags();
|
||||
flags.AllowWindowsPath();
|
||||
flags.AllowRelativePath();
|
||||
flags.AllowEmptyPath();
|
||||
|
||||
rc = pathNormalized.Normalize(flags);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = _rootPath.Initialize(in pathNormalized);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
_rootPathUtf16 = _rootPath.ToString();
|
||||
|
||||
pathNormalized.Dispose();
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
private Result CheckSubPath(U8Span path1, U8Span path2)
|
||||
private Result ResolveFullPath(out string outFullPath, in Path path, bool checkCaseSensitivity)
|
||||
{
|
||||
Unsafe.SkipInit(out FsPath normalizedPath1);
|
||||
Unsafe.SkipInit(out FsPath normalizedPath2);
|
||||
UnsafeHelpers.SkipParamInit(out outFullPath);
|
||||
|
||||
Result rc = PathNormalizer.Normalize(normalizedPath1.Str, out _, path1, false, false);
|
||||
// Always normalize the incoming path even if it claims to already be normalized
|
||||
// because we don't want to allow access to anything outside the root path.
|
||||
|
||||
var pathNormalized = new Path();
|
||||
Result rc = pathNormalized.Initialize(path.GetString());
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = PathNormalizer.Normalize(normalizedPath2.Str, out _, path2, false, false);
|
||||
var pathFlags = new PathFlags();
|
||||
pathFlags.AllowWindowsPath();
|
||||
pathFlags.AllowRelativePath();
|
||||
pathFlags.AllowEmptyPath();
|
||||
rc = pathNormalized.Normalize(pathFlags);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (PathUtility.IsSubPath(normalizedPath1, normalizedPath2))
|
||||
Path rootPath = _rootPath.GetPath();
|
||||
|
||||
var fullPath = new Path();
|
||||
rc = fullPath.Combine(in rootPath, in pathNormalized);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
string utf16FullPath = fullPath.ToString();
|
||||
|
||||
if (_mode == PathMode.CaseSensitive && checkCaseSensitivity)
|
||||
{
|
||||
return ResultFs.DirectoryNotRenamable.Log();
|
||||
rc = CheckPathCaseSensitively(utf16FullPath);
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
|
||||
outFullPath = utf16FullPath;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
protected override Result DoGetFileAttributes(out NxFileAttributes attributes, U8Span path)
|
||||
protected override Result DoGetFileAttributes(out NxFileAttributes attributes, in Path path)
|
||||
{
|
||||
UnsafeHelpers.SkipParamInit(out attributes);
|
||||
|
||||
Result rc = ResolveFullPath(out string fullPath, path, true);
|
||||
Result rc = ResolveFullPath(out string fullPath, in path, true);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = GetFileInfo(out FileInfo info, fullPath);
|
||||
@ -186,9 +213,9 @@ namespace LibHac.FsSystem
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
protected override Result DoSetFileAttributes(U8Span path, NxFileAttributes attributes)
|
||||
protected override Result DoSetFileAttributes(in Path path, NxFileAttributes attributes)
|
||||
{
|
||||
Result rc = ResolveFullPath(out string fullPath, path, true);
|
||||
Result rc = ResolveFullPath(out string fullPath, in path, true);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = GetFileInfo(out FileInfo info, fullPath);
|
||||
@ -214,11 +241,11 @@ namespace LibHac.FsSystem
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
protected override Result DoGetFileSize(out long fileSize, U8Span path)
|
||||
protected override Result DoGetFileSize(out long fileSize, in Path path)
|
||||
{
|
||||
UnsafeHelpers.SkipParamInit(out fileSize);
|
||||
|
||||
Result rc = ResolveFullPath(out string fullPath, path, true);
|
||||
Result rc = ResolveFullPath(out string fullPath, in path, true);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = GetFileInfo(out FileInfo info, fullPath);
|
||||
@ -232,9 +259,9 @@ namespace LibHac.FsSystem
|
||||
return DoCreateDirectory(path, NxFileAttributes.None);
|
||||
}
|
||||
|
||||
protected override Result DoCreateDirectory(U8Span path, NxFileAttributes archiveAttribute)
|
||||
protected override Result DoCreateDirectory(in Path path, NxFileAttributes archiveAttribute)
|
||||
{
|
||||
Result rc = ResolveFullPath(out string fullPath, path, false);
|
||||
Result rc = ResolveFullPath(out string fullPath, in path, false);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = GetDirInfo(out DirectoryInfo dir, fullPath);
|
||||
@ -255,7 +282,7 @@ namespace LibHac.FsSystem
|
||||
|
||||
protected override Result DoCreateFile(in Path path, long size, CreateFileOptions option)
|
||||
{
|
||||
Result rc = ResolveFullPath(out string fullPath, path, false);
|
||||
Result rc = ResolveFullPath(out string fullPath, in path, false);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = GetFileInfo(out FileInfo file, fullPath);
|
||||
@ -283,7 +310,7 @@ namespace LibHac.FsSystem
|
||||
|
||||
protected override Result DoDeleteDirectory(in Path path)
|
||||
{
|
||||
Result rc = ResolveFullPath(out string fullPath, path, true);
|
||||
Result rc = ResolveFullPath(out string fullPath, in path, true);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = GetDirInfo(out DirectoryInfo dir, fullPath);
|
||||
@ -295,7 +322,7 @@ namespace LibHac.FsSystem
|
||||
|
||||
protected override Result DoDeleteDirectoryRecursively(in Path path)
|
||||
{
|
||||
Result rc = ResolveFullPath(out string fullPath, path, true);
|
||||
Result rc = ResolveFullPath(out string fullPath, in path, true);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = GetDirInfo(out DirectoryInfo dir, fullPath);
|
||||
@ -307,7 +334,7 @@ namespace LibHac.FsSystem
|
||||
|
||||
protected override Result DoCleanDirectoryRecursively(in Path path)
|
||||
{
|
||||
Result rc = ResolveFullPath(out string fullPath, path, true);
|
||||
Result rc = ResolveFullPath(out string fullPath, in path, true);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
foreach (string file in Directory.EnumerateFiles(fullPath))
|
||||
@ -343,7 +370,7 @@ namespace LibHac.FsSystem
|
||||
|
||||
protected override Result DoDeleteFile(in Path path)
|
||||
{
|
||||
Result rc = ResolveFullPath(out string fullPath, path, true);
|
||||
Result rc = ResolveFullPath(out string fullPath, in path, true);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = GetFileInfo(out FileInfo file, fullPath);
|
||||
@ -356,7 +383,7 @@ namespace LibHac.FsSystem
|
||||
protected override Result DoOpenDirectory(out IDirectory directory, in Path path, OpenDirectoryMode mode)
|
||||
{
|
||||
UnsafeHelpers.SkipParamInit(out directory);
|
||||
Result rc = ResolveFullPath(out string fullPath, path, true);
|
||||
Result rc = ResolveFullPath(out string fullPath, in path, true);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = GetDirInfo(out DirectoryInfo dirInfo, fullPath);
|
||||
@ -380,7 +407,7 @@ namespace LibHac.FsSystem
|
||||
{
|
||||
UnsafeHelpers.SkipParamInit(out file);
|
||||
|
||||
Result rc = ResolveFullPath(out string fullPath, path, true);
|
||||
Result rc = ResolveFullPath(out string fullPath, in path, true);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = GetEntryType(out DirectoryEntryType entryType, path);
|
||||
@ -403,13 +430,10 @@ namespace LibHac.FsSystem
|
||||
|
||||
protected override Result DoRenameDirectory(in Path currentPath, in Path newPath)
|
||||
{
|
||||
Result rc = CheckSubPath(currentPath, newPath);
|
||||
Result rc = ResolveFullPath(out string fullCurrentPath, in currentPath, true);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = ResolveFullPath(out string fullCurrentPath, currentPath, true);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = ResolveFullPath(out string fullNewPath, newPath, false);
|
||||
rc = ResolveFullPath(out string fullNewPath, in newPath, false);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
// Official FS behavior is to do nothing in this case
|
||||
@ -427,10 +451,10 @@ namespace LibHac.FsSystem
|
||||
|
||||
protected override Result DoRenameFile(in Path currentPath, in Path newPath)
|
||||
{
|
||||
Result rc = ResolveFullPath(out string fullCurrentPath, currentPath, true);
|
||||
Result rc = ResolveFullPath(out string fullCurrentPath, in currentPath, true);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = ResolveFullPath(out string fullNewPath, newPath, false);
|
||||
rc = ResolveFullPath(out string fullNewPath, in newPath, false);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
// Official FS behavior is to do nothing in this case
|
||||
@ -450,7 +474,7 @@ namespace LibHac.FsSystem
|
||||
{
|
||||
UnsafeHelpers.SkipParamInit(out entryType);
|
||||
|
||||
Result rc = ResolveFullPath(out string fullPath, path, true);
|
||||
Result rc = ResolveFullPath(out string fullPath, in path, true);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = GetDirInfo(out DirectoryInfo dir, fullPath);
|
||||
@ -478,7 +502,7 @@ namespace LibHac.FsSystem
|
||||
{
|
||||
UnsafeHelpers.SkipParamInit(out timeStamp);
|
||||
|
||||
Result rc = ResolveFullPath(out string fullPath, path, true);
|
||||
Result rc = ResolveFullPath(out string fullPath, in path, true);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = GetFileInfo(out FileInfo file, fullPath);
|
||||
@ -508,7 +532,7 @@ namespace LibHac.FsSystem
|
||||
{
|
||||
UnsafeHelpers.SkipParamInit(out freeSpace);
|
||||
|
||||
Result rc = ResolveFullPath(out string fullPath, path, true);
|
||||
Result rc = ResolveFullPath(out string fullPath, in path, true);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
freeSpace = new DriveInfo(fullPath).AvailableFreeSpace;
|
||||
@ -519,7 +543,7 @@ namespace LibHac.FsSystem
|
||||
{
|
||||
UnsafeHelpers.SkipParamInit(out totalSpace);
|
||||
|
||||
Result rc = ResolveFullPath(out string fullPath, path, true);
|
||||
Result rc = ResolveFullPath(out string fullPath, in path, true);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
totalSpace = new DriveInfo(fullPath).TotalSize;
|
||||
@ -802,7 +826,7 @@ namespace LibHac.FsSystem
|
||||
|
||||
private Result CheckPathCaseSensitively(string path)
|
||||
{
|
||||
Result rc = GetCaseSensitivePathFull(out string caseSensitivePath, out _, path, _rootPath);
|
||||
Result rc = GetCaseSensitivePathFull(out string caseSensitivePath, out _, path, _rootPathUtf16);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (path.Length != caseSensitivePath.Length)
|
||||
|
@ -42,9 +42,9 @@ namespace LibHac.FsSystem
|
||||
|
||||
protected override Result DoOpenFile(out IFile file, in Path path, OpenMode mode)
|
||||
{
|
||||
path = PathTools.Normalize(path.ToString()).TrimStart('/').ToU8Span();
|
||||
string pathNormalized = PathTools.Normalize(path.ToString()).TrimStart('/');
|
||||
|
||||
if (!FileDict.TryGetValue(path.ToString(), out PartitionFileEntry entry))
|
||||
if (!FileDict.TryGetValue(pathNormalized, out PartitionFileEntry entry))
|
||||
{
|
||||
ThrowHelper.ThrowResult(ResultFs.PathNotFound.Value);
|
||||
}
|
||||
|
@ -58,7 +58,7 @@ namespace LibHac.FsSystem
|
||||
|
||||
ReadOnlySpan<byte> rootPath = new[] { (byte)'/' };
|
||||
|
||||
if (StringUtils.Compare(rootPath, path, 2) != 0)
|
||||
if (path == rootPath)
|
||||
return ResultFs.PathNotFound.Log();
|
||||
|
||||
directory = new PartitionDirectory(this, mode);
|
||||
@ -76,7 +76,7 @@ namespace LibHac.FsSystem
|
||||
if (!mode.HasFlag(OpenMode.Read) && !mode.HasFlag(OpenMode.Write))
|
||||
return ResultFs.InvalidArgument.Log();
|
||||
|
||||
int entryIndex = MetaData.FindEntry(path.Slice(1));
|
||||
int entryIndex = MetaData.FindEntry(new U8Span(path.GetString().Slice(1)));
|
||||
if (entryIndex < 0) return ResultFs.PathNotFound.Log();
|
||||
|
||||
ref T entry = ref MetaData.GetEntry(entryIndex);
|
||||
@ -93,18 +93,20 @@ namespace LibHac.FsSystem
|
||||
if (!IsInitialized)
|
||||
return ResultFs.PreconditionViolation.Log();
|
||||
|
||||
if (path.IsEmpty() || path[0] != '/')
|
||||
ReadOnlySpan<byte> pathStr = path.GetString();
|
||||
|
||||
if (path.IsEmpty() || pathStr[0] != '/')
|
||||
return ResultFs.InvalidPathFormat.Log();
|
||||
|
||||
ReadOnlySpan<byte> rootPath = new[] { (byte)'/' };
|
||||
|
||||
if (StringUtils.Compare(rootPath, path, 2) == 0)
|
||||
if (StringUtils.Compare(rootPath, pathStr, 2) == 0)
|
||||
{
|
||||
entryType = DirectoryEntryType.Directory;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
if (MetaData.FindEntry(path.Slice(1)) >= 0)
|
||||
if (MetaData.FindEntry(new U8Span(pathStr.Slice(1))) >= 0)
|
||||
{
|
||||
entryType = DirectoryEntryType.File;
|
||||
return Result.Success;
|
||||
|
@ -3,6 +3,7 @@ using System.Diagnostics;
|
||||
using System.IO.Enumeration;
|
||||
using System.Runtime.CompilerServices;
|
||||
using LibHac.Common;
|
||||
using LibHac.Diag;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Util;
|
||||
|
||||
@ -235,7 +236,7 @@ namespace LibHac.FsSystem
|
||||
|
||||
public static ReadOnlySpan<byte> GetParentDirectory(ReadOnlySpan<byte> path)
|
||||
{
|
||||
Debug.Assert(IsNormalized(path));
|
||||
Assert.SdkAssert(IsNormalized(path));
|
||||
|
||||
int i = StringUtils.GetLength(path) - 1;
|
||||
|
||||
@ -288,6 +289,9 @@ namespace LibHac.FsSystem
|
||||
|
||||
foreach (char c in path)
|
||||
{
|
||||
if (c == 0)
|
||||
break;
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case NormalizeState.Initial when c == '/': state = NormalizeState.Delimiter; break;
|
||||
@ -325,6 +329,9 @@ namespace LibHac.FsSystem
|
||||
|
||||
foreach (byte c in path)
|
||||
{
|
||||
if (c == 0)
|
||||
break;
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case NormalizeState.Initial when c == '/': state = NormalizeState.Delimiter; break;
|
||||
|
@ -1,96 +0,0 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using LibHac.Common;
|
||||
using LibHac.Diag;
|
||||
using LibHac.Os;
|
||||
|
||||
namespace LibHac.FsSystem
|
||||
{
|
||||
public interface IUniqueLock : IDisposable
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a lock that may be passed between functions or objects.
|
||||
/// </summary>
|
||||
/// <remarks>This struct must never be copied. It must always be passed by
|
||||
/// reference or moved via the move constructor.</remarks>
|
||||
public struct UniqueLockSemaphore : IDisposable
|
||||
{
|
||||
private SemaphoreAdapter _semaphore;
|
||||
private bool _isLocked;
|
||||
|
||||
public UniqueLockSemaphore(SemaphoreAdapter semaphore)
|
||||
{
|
||||
_semaphore = semaphore;
|
||||
_isLocked = false;
|
||||
}
|
||||
|
||||
public UniqueLockSemaphore(ref UniqueLockSemaphore other)
|
||||
{
|
||||
_semaphore = other._semaphore;
|
||||
_isLocked = other._isLocked;
|
||||
|
||||
other._isLocked = false;
|
||||
other._semaphore = null;
|
||||
}
|
||||
|
||||
public bool IsLocked => _isLocked;
|
||||
|
||||
public bool TryLock()
|
||||
{
|
||||
if (_isLocked)
|
||||
{
|
||||
throw new SynchronizationLockException("Attempted to lock a UniqueLock that was already locked.");
|
||||
}
|
||||
|
||||
_isLocked = _semaphore.TryLock();
|
||||
return _isLocked;
|
||||
}
|
||||
|
||||
public void Unlock()
|
||||
{
|
||||
if (!_isLocked)
|
||||
{
|
||||
throw new SynchronizationLockException("Attempted to unlock a UniqueLock that was not locked.");
|
||||
}
|
||||
|
||||
_semaphore.Unlock();
|
||||
_isLocked = false;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isLocked)
|
||||
{
|
||||
_semaphore.Unlock();
|
||||
|
||||
_isLocked = false;
|
||||
_semaphore = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class UniqueLockWithPin<T> : IUniqueLock where T : class, IDisposable
|
||||
{
|
||||
private UniqueLock<SemaphoreAdapter> _semaphore;
|
||||
private ReferenceCountedDisposable<T> _pinnedObject;
|
||||
|
||||
public UniqueLockWithPin(ref UniqueLock<SemaphoreAdapter> semaphore, ref ReferenceCountedDisposable<T> pinnedObject)
|
||||
{
|
||||
Shared.Move(out _semaphore, ref semaphore);
|
||||
Shared.Move(out _pinnedObject, ref pinnedObject);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_pinnedObject != null)
|
||||
{
|
||||
_semaphore.Dispose();
|
||||
_pinnedObject.Dispose();
|
||||
|
||||
_pinnedObject = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,270 +0,0 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using LibHac.Common;
|
||||
using LibHac.Diag;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs.Common;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.Util;
|
||||
|
||||
namespace LibHac.FsSystem
|
||||
{
|
||||
internal static class Utility
|
||||
{
|
||||
public delegate Result FsIterationTask(U8Span path, ref DirectoryEntry entry);
|
||||
|
||||
private static U8Span RootPath => new U8Span(new[] { (byte)'/' });
|
||||
private static U8Span DirectorySeparator => RootPath;
|
||||
|
||||
public static Result IterateDirectoryRecursively(IFileSystem fs, U8Span rootPath, Span<byte> workPath,
|
||||
ref DirectoryEntry dirEntry, FsIterationTask onEnterDir, FsIterationTask onExitDir, FsIterationTask onFile)
|
||||
{
|
||||
Abort.DoAbortUnless(workPath.Length >= PathTool.EntryNameLengthMax + 1);
|
||||
|
||||
// Get size of the root path.
|
||||
int rootPathLen = StringUtils.GetLength(rootPath, PathTool.EntryNameLengthMax + 1);
|
||||
if (rootPathLen > PathTool.EntryNameLengthMax)
|
||||
return ResultFs.TooLongPath.Log();
|
||||
|
||||
// Copy root path in, add a / if necessary.
|
||||
rootPath.Value.Slice(0, rootPathLen).CopyTo(workPath);
|
||||
if (workPath[rootPathLen - 1] != StringTraits.DirectorySeparator)
|
||||
{
|
||||
workPath[rootPathLen++] = StringTraits.DirectorySeparator;
|
||||
}
|
||||
|
||||
// Make sure the result path is still valid.
|
||||
if (rootPathLen > PathTool.EntryNameLengthMax)
|
||||
return ResultFs.TooLongPath.Log();
|
||||
|
||||
workPath[rootPathLen] = StringTraits.NullTerminator;
|
||||
|
||||
return IterateDirectoryRecursivelyImpl(fs, workPath, ref dirEntry, onEnterDir, onExitDir, onFile);
|
||||
}
|
||||
|
||||
public static Result IterateDirectoryRecursively(IFileSystem fs, U8Span rootPath, FsIterationTask onEnterDir,
|
||||
FsIterationTask onExitDir, FsIterationTask onFile)
|
||||
{
|
||||
var entry = new DirectoryEntry();
|
||||
Span<byte> workPath = stackalloc byte[PathTools.MaxPathLength + 1];
|
||||
|
||||
return IterateDirectoryRecursively(fs, rootPath, workPath, ref entry, onEnterDir, onExitDir,
|
||||
onFile);
|
||||
}
|
||||
|
||||
public static Result IterateDirectoryRecursively(IFileSystem fs, FsIterationTask onEnterDir,
|
||||
FsIterationTask onExitDir, FsIterationTask onFile)
|
||||
{
|
||||
return IterateDirectoryRecursively(fs, RootPath, onEnterDir, onExitDir, onFile);
|
||||
}
|
||||
|
||||
private static Result IterateDirectoryRecursivelyImpl(IFileSystem fs, Span<byte> workPath,
|
||||
ref DirectoryEntry dirEntry, FsIterationTask onEnterDir, FsIterationTask onExitDir, FsIterationTask onFile)
|
||||
{
|
||||
Result rc = fs.OpenDirectory(out IDirectory dir, new U8Span(workPath), OpenDirectoryMode.All);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
int parentLen = StringUtils.GetLength(workPath);
|
||||
|
||||
// Read and handle entries.
|
||||
while (true)
|
||||
{
|
||||
// Read a single entry.
|
||||
rc = dir.Read(out long readCount, SpanHelpers.AsSpan(ref dirEntry));
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
// If we're out of entries, we're done.
|
||||
if (readCount == 0)
|
||||
break;
|
||||
|
||||
// Validate child path size.
|
||||
int childNameLen = StringUtils.GetLength(dirEntry.Name);
|
||||
bool isDir = dirEntry.Type == DirectoryEntryType.Directory;
|
||||
int separatorSize = isDir ? 1 : 0;
|
||||
|
||||
if (parentLen + childNameLen + separatorSize >= workPath.Length)
|
||||
return ResultFs.TooLongPath.Log();
|
||||
|
||||
// Set child path.
|
||||
StringUtils.Concat(workPath, dirEntry.Name);
|
||||
{
|
||||
if (isDir)
|
||||
{
|
||||
// Enter directory.
|
||||
rc = onEnterDir(new U8Span(workPath), ref dirEntry);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
// Append separator, recurse.
|
||||
StringUtils.Concat(workPath, DirectorySeparator);
|
||||
|
||||
rc = IterateDirectoryRecursivelyImpl(fs, workPath, ref dirEntry, onEnterDir, onExitDir, onFile);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
// Exit directory.
|
||||
rc = onExitDir(new U8Span(workPath), ref dirEntry);
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Call file handler.
|
||||
rc = onFile(new U8Span(workPath), ref dirEntry);
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
}
|
||||
|
||||
// Restore parent path.
|
||||
workPath[parentLen] = StringTraits.NullTerminator;
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public static Result CopyDirectoryRecursively(IFileSystem fileSystem, U8Span destPath, U8Span sourcePath,
|
||||
Span<byte> workBuffer)
|
||||
{
|
||||
return CopyDirectoryRecursively(fileSystem, fileSystem, destPath, sourcePath, workBuffer);
|
||||
}
|
||||
|
||||
public static unsafe Result CopyDirectoryRecursively(IFileSystem destFileSystem, IFileSystem sourceFileSystem,
|
||||
U8Span destPath, U8Span sourcePath, Span<byte> workBuffer)
|
||||
{
|
||||
var destPathBuf = new FsPath();
|
||||
int originalSize = StringUtils.Copy(destPathBuf.Str, destPath);
|
||||
Abort.DoAbortUnless(originalSize < Unsafe.SizeOf<FsPath>());
|
||||
|
||||
// Pin and recreate the span because C# can't use byref-like types in a closure
|
||||
int workBufferSize = workBuffer.Length;
|
||||
fixed (byte* pWorkBuffer = workBuffer)
|
||||
{
|
||||
// Copy the pointer to workaround CS1764.
|
||||
// IterateDirectoryRecursively won't store the delegate anywhere, so it should be safe
|
||||
byte* pWorkBuffer2 = pWorkBuffer;
|
||||
|
||||
Result OnEnterDir(U8Span path, ref DirectoryEntry entry)
|
||||
{
|
||||
// Update path, create new dir.
|
||||
StringUtils.Concat(SpanHelpers.AsByteSpan(ref destPathBuf), entry.Name);
|
||||
StringUtils.Concat(SpanHelpers.AsByteSpan(ref destPathBuf), DirectorySeparator);
|
||||
|
||||
return destFileSystem.CreateDirectory(destPathBuf);
|
||||
}
|
||||
|
||||
Result OnExitDir(U8Span path, ref DirectoryEntry entry)
|
||||
{
|
||||
// Check we have a parent directory.
|
||||
int len = StringUtils.GetLength(SpanHelpers.AsByteSpan(ref destPathBuf));
|
||||
if (len < 2)
|
||||
return ResultFs.InvalidPathFormat.Log();
|
||||
|
||||
// Find previous separator, add null terminator
|
||||
int cur = len - 2;
|
||||
while (SpanHelpers.AsByteSpan(ref destPathBuf)[cur] != StringTraits.DirectorySeparator && cur > 0)
|
||||
{
|
||||
cur--;
|
||||
}
|
||||
|
||||
SpanHelpers.AsByteSpan(ref destPathBuf)[cur + 1] = StringTraits.NullTerminator;
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
Result OnFile(U8Span path, ref DirectoryEntry entry)
|
||||
{
|
||||
var buffer = new Span<byte>(pWorkBuffer2, workBufferSize);
|
||||
|
||||
return CopyFile(destFileSystem, sourceFileSystem, destPathBuf, path, ref entry, buffer);
|
||||
}
|
||||
|
||||
return IterateDirectoryRecursively(sourceFileSystem, sourcePath, OnEnterDir, OnExitDir, OnFile);
|
||||
}
|
||||
}
|
||||
|
||||
public static Result CopyFile(IFileSystem destFileSystem, IFileSystem sourceFileSystem, U8Span destParentPath,
|
||||
U8Span sourcePath, ref DirectoryEntry entry, Span<byte> workBuffer)
|
||||
{
|
||||
// Open source file.
|
||||
Result rc = sourceFileSystem.OpenFile(out IFile sourceFile, sourcePath, OpenMode.Read);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
using (sourceFile)
|
||||
{
|
||||
// Open dest file.
|
||||
Unsafe.SkipInit(out FsPath destPath);
|
||||
|
||||
var sb = new U8StringBuilder(destPath.Str);
|
||||
sb.Append(destParentPath).Append(entry.Name);
|
||||
|
||||
Assert.SdkLess(sb.Length, Unsafe.SizeOf<FsPath>());
|
||||
|
||||
rc = destFileSystem.CreateFile(new U8Span(destPath.Str), entry.Size);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = destFileSystem.OpenFile(out IFile destFile, new U8Span(destPath.Str), OpenMode.Write);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
using (destFile)
|
||||
{
|
||||
// Read/Write file in work buffer sized chunks.
|
||||
long remaining = entry.Size;
|
||||
long offset = 0;
|
||||
|
||||
while (remaining > 0)
|
||||
{
|
||||
rc = sourceFile.Read(out long bytesRead, offset, workBuffer, ReadOption.None);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = destFile.Write(offset, workBuffer.Slice(0, (int)bytesRead), WriteOption.None);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
remaining -= bytesRead;
|
||||
offset += bytesRead;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public static Result TryAcquireCountSemaphore(out UniqueLockSemaphore uniqueLock, SemaphoreAdapter semaphore)
|
||||
{
|
||||
UniqueLockSemaphore tempUniqueLock = default;
|
||||
try
|
||||
{
|
||||
tempUniqueLock = new UniqueLockSemaphore(semaphore);
|
||||
|
||||
if (!tempUniqueLock.TryLock())
|
||||
{
|
||||
UnsafeHelpers.SkipParamInit(out uniqueLock);
|
||||
return ResultFs.OpenCountLimit.Log();
|
||||
}
|
||||
|
||||
uniqueLock = new UniqueLockSemaphore(ref tempUniqueLock);
|
||||
return Result.Success;
|
||||
}
|
||||
finally
|
||||
{
|
||||
tempUniqueLock.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public static Result MakeUniqueLockWithPin<T>(out IUniqueLock uniqueLock, SemaphoreAdapter semaphore,
|
||||
ref ReferenceCountedDisposable<T> objectToPin) where T : class, IDisposable
|
||||
{
|
||||
UnsafeHelpers.SkipParamInit(out uniqueLock);
|
||||
|
||||
UniqueLockSemaphore tempUniqueLock = default;
|
||||
try
|
||||
{
|
||||
Result rc = TryAcquireCountSemaphore(out tempUniqueLock, semaphore);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
uniqueLock = new UniqueLockWithPin<T>(ref tempUniqueLock, ref objectToPin);
|
||||
return Result.Success;
|
||||
}
|
||||
finally
|
||||
{
|
||||
tempUniqueLock.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -433,7 +433,7 @@ namespace LibHac.FsSystem
|
||||
UniqueLock<SemaphoreAdapter> tempUniqueLock = default;
|
||||
try
|
||||
{
|
||||
tempUniqueLock = new UniqueLock<SemaphoreAdapter>(semaphore);
|
||||
tempUniqueLock = new UniqueLock<SemaphoreAdapter>(semaphore, new DeferLock());
|
||||
|
||||
if (!tempUniqueLock.TryLock())
|
||||
{
|
||||
|
@ -8,7 +8,7 @@ namespace LibHac.Lr
|
||||
internal struct LrServiceGlobals
|
||||
{
|
||||
public ILocationResolverManager LocationResolver;
|
||||
public SdkMutex InitializationMutex;
|
||||
public SdkMutexType InitializationMutex;
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
@ -34,7 +34,7 @@ namespace LibHac.Lr
|
||||
Assert.SdkRequiresNotNull(globals.LocationResolver);
|
||||
|
||||
// The lock over getting the service object is a LibHac addition.
|
||||
using ScopedLock<SdkMutex> scopedLock = ScopedLock.Lock(ref lr.Globals.LrService.InitializationMutex);
|
||||
using ScopedLock<SdkMutexType> scopedLock = ScopedLock.Lock(ref lr.Globals.LrService.InitializationMutex);
|
||||
|
||||
if (globals.LocationResolver is not null)
|
||||
return;
|
||||
|
@ -5,6 +5,12 @@ using LibHac.Common;
|
||||
|
||||
namespace LibHac.Os
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies that a constructed <see cref="UniqueLock{TMutex}"/> should not be automatically locked upon construction.<br/>
|
||||
/// Used only to differentiate between <see cref="UniqueLock{TMutex}"/> constructor signatures.
|
||||
/// </summary>
|
||||
public struct DeferLock { }
|
||||
|
||||
public static class UniqueLock
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@ -33,6 +39,13 @@ namespace LibHac.Os
|
||||
_ownsLock = true;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public UniqueLockRef(ref TMutex mutex, DeferLock tag)
|
||||
{
|
||||
_mutex = new Ref<TMutex>(ref mutex);
|
||||
_ownsLock = false;
|
||||
}
|
||||
|
||||
public UniqueLockRef(ref UniqueLockRef<TMutex> other)
|
||||
{
|
||||
this = other;
|
||||
@ -99,11 +112,18 @@ namespace LibHac.Os
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public UniqueLock(TMutex mutex)
|
||||
{
|
||||
_mutex = new Ref<TMutex>(ref mutex);
|
||||
_mutex = mutex;
|
||||
mutex.Lock();
|
||||
_ownsLock = true;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public UniqueLock(TMutex mutex, DeferLock tag)
|
||||
{
|
||||
_mutex = mutex;
|
||||
_ownsLock = false;
|
||||
}
|
||||
|
||||
public UniqueLock(ref UniqueLock<TMutex> other)
|
||||
{
|
||||
this = other;
|
||||
|
@ -44,14 +44,20 @@ namespace LibHac
|
||||
{
|
||||
var concatFs = new ConcatenationFileSystem(fileSystem);
|
||||
|
||||
var contentDirPath = new Fs.Path();
|
||||
PathFunctions.SetUpFixedPath(ref contentDirPath, "/Nintendo/Contents".ToU8String()).ThrowIfFailure();
|
||||
|
||||
var saveDirPath = new Fs.Path();
|
||||
PathFunctions.SetUpFixedPath(ref saveDirPath, "/Nintendo/save".ToU8String()).ThrowIfFailure();
|
||||
|
||||
var contentDirFs = new SubdirectoryFileSystem(concatFs);
|
||||
contentDirFs.Initialize("/Nintendo/Contents".ToU8String()).ThrowIfFailure();
|
||||
contentDirFs.Initialize(in contentDirPath).ThrowIfFailure();
|
||||
|
||||
AesXtsFileSystem encSaveFs = null;
|
||||
if (fileSystem.DirectoryExists("/Nintendo/save"))
|
||||
{
|
||||
var saveDirFs = new SubdirectoryFileSystem(concatFs);
|
||||
saveDirFs.Initialize("/Nintendo/save".ToU8String()).ThrowIfFailure();
|
||||
saveDirFs.Initialize(in saveDirPath).ThrowIfFailure();
|
||||
|
||||
encSaveFs = new AesXtsFileSystem(saveDirFs, keySet.SdCardEncryptionKeys[0].DataRo.ToArray(), 0x4000);
|
||||
}
|
||||
@ -65,7 +71,7 @@ namespace LibHac
|
||||
{
|
||||
var concatFs = new ConcatenationFileSystem(fileSystem);
|
||||
SubdirectoryFileSystem saveDirFs = null;
|
||||
SubdirectoryFileSystem contentDirFs = null;
|
||||
SubdirectoryFileSystem contentDirFs;
|
||||
|
||||
if (concatFs.DirectoryExists("/save"))
|
||||
{
|
||||
|
@ -15,7 +15,7 @@ namespace hactoolnet
|
||||
return;
|
||||
}
|
||||
|
||||
var localFs = new LocalFileSystem(ctx.Options.InFile);
|
||||
LocalFileSystem.Create(out LocalFileSystem localFs, ctx.Options.InFile).ThrowIfFailure();
|
||||
|
||||
var builder = new RomFsBuilder(localFs);
|
||||
IStorage romFs = builder.Build();
|
||||
|
@ -43,8 +43,8 @@ namespace LibHac.Tests.Fs.FileSystemClientTests.ShimTests
|
||||
var applicationId = new Ncm.ApplicationId(1);
|
||||
FileSystemClient fs = FileSystemServerFactory.CreateClient(true);
|
||||
|
||||
fs.CreateCacheStorage(applicationId, SaveDataSpaceId.SdCache, applicationId.Value, 0, 0, SaveDataFlags.None);
|
||||
fs.MountCacheStorage("cache".ToU8Span(), applicationId);
|
||||
Assert.Success(fs.CreateCacheStorage(applicationId, SaveDataSpaceId.SdCache, applicationId.Value, 0, 0, SaveDataFlags.None));
|
||||
Assert.Success(fs.MountCacheStorage("cache".ToU8Span(), applicationId));
|
||||
fs.CreateFile("cache:/sd".ToU8Span(), 0);
|
||||
fs.Commit("cache".ToU8Span());
|
||||
fs.Unmount("cache".ToU8Span());
|
||||
|
@ -1,13 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LibHac.Tests.FsSystem
|
||||
namespace LibHac.Tests.FsSystem
|
||||
{
|
||||
class ConcatenationFileSystemTests
|
||||
public class ConcatenationFileSystemTests
|
||||
{
|
||||
asdf
|
||||
//asdf
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using Xunit.Sdk;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace Xunit
|
||||
{
|
||||
public partial class Assert
|
||||
|
Loading…
x
Reference in New Issue
Block a user