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];
|
_replacedChar = _buffer[1];
|
||||||
_buffer[1] = 0;
|
_buffer[1] = 0;
|
||||||
|
_position = 1;
|
||||||
return _buffer;
|
return _buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -538,12 +538,12 @@ namespace LibHac.Fs
|
|||||||
int parentBytesCopied = StringUtils.Copy(destBuffer, parent, parentLength + SeparatorLength);
|
int parentBytesCopied = StringUtils.Copy(destBuffer, parent, parentLength + SeparatorLength);
|
||||||
|
|
||||||
// Make sure we copied the expected number of parent bytes.
|
// 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();
|
return ResultFs.UnexpectedInPathA.Log();
|
||||||
}
|
}
|
||||||
else if (parentBytesCopied != parentLength)
|
else if (parentBytesCopied != parentLength + SeparatorLength)
|
||||||
{
|
{
|
||||||
return ResultFs.UnexpectedInPathA.Log();
|
return ResultFs.UnexpectedInPathA.Log();
|
||||||
}
|
}
|
||||||
@ -602,7 +602,7 @@ namespace LibHac.Fs
|
|||||||
if (_string[parentLength - 1] == DirectorySeparator || _string[parentLength - 1] == AltDirectorySeparator)
|
if (_string[parentLength - 1] == DirectorySeparator || _string[parentLength - 1] == AltDirectorySeparator)
|
||||||
parentLength--;
|
parentLength--;
|
||||||
|
|
||||||
int childLength = StringUtils.GetLength(child);
|
int childLength = StringUtils.GetLength(trimmedChild);
|
||||||
|
|
||||||
byte[] parentBuffer = null;
|
byte[] parentBuffer = null;
|
||||||
try
|
try
|
||||||
@ -630,15 +630,14 @@ namespace LibHac.Fs
|
|||||||
|
|
||||||
if (childBytesCopied != childLength)
|
if (childBytesCopied != childLength)
|
||||||
return ResultFs.UnexpectedInPathA.Log();
|
return ResultFs.UnexpectedInPathA.Log();
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
if (parentBuffer is not null)
|
if (parentBuffer is not null)
|
||||||
ArrayPool<byte>.Shared.Return(parentBuffer);
|
ArrayPool<byte>.Shared.Return(parentBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
_isNormalized = false;
|
|
||||||
return Result.Success;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result AppendChild(in Path child)
|
public Result AppendChild(in Path child)
|
||||||
@ -737,7 +736,6 @@ namespace LibHac.Fs
|
|||||||
if (currentPos <= 0)
|
if (currentPos <= 0)
|
||||||
return ResultFs.NotImplemented.Log();
|
return ResultFs.NotImplemented.Log();
|
||||||
|
|
||||||
_isNormalized = false;
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,12 +44,6 @@ namespace LibHac.Fs.Fsa
|
|||||||
protected abstract Result DoRead(out long entriesRead, Span<DirectoryEntry> entryBuffer);
|
protected abstract Result DoRead(out long entriesRead, Span<DirectoryEntry> entryBuffer);
|
||||||
protected abstract Result DoGetEntryCount(out long entryCount);
|
protected abstract Result DoGetEntryCount(out long entryCount);
|
||||||
|
|
||||||
protected virtual void Dispose(bool disposing) { }
|
public virtual void Dispose() { }
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
Dispose(true);
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -219,12 +219,6 @@ namespace LibHac.Fs.Fsa
|
|||||||
protected abstract Result DoOperateRange(Span<byte> outBuffer, OperationId operationId, long offset, long size,
|
protected abstract Result DoOperateRange(Span<byte> outBuffer, OperationId operationId, long offset, long size,
|
||||||
ReadOnlySpan<byte> inBuffer);
|
ReadOnlySpan<byte> inBuffer);
|
||||||
|
|
||||||
protected virtual void Dispose(bool disposing) { }
|
public virtual void Dispose() { }
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
Dispose(true);
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,14 +31,11 @@ namespace LibHac.Fs.Impl
|
|||||||
BaseFile = baseFile.AddReference();
|
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)
|
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();
|
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)
|
protected override Result DoRead(out long entriesRead, Span<DirectoryEntry> entryBuffer)
|
||||||
|
@ -7,7 +7,7 @@ using LibHac.FsSystem;
|
|||||||
using LibHac.Sf;
|
using LibHac.Sf;
|
||||||
using IFileSystem = LibHac.Fs.Fsa.IFileSystem;
|
using IFileSystem = LibHac.Fs.Fsa.IFileSystem;
|
||||||
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
|
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
|
||||||
using PathNormalizer = LibHac.FsSrv.Impl.PathNormalizer;
|
using Path = LibHac.Fs.Path;
|
||||||
|
|
||||||
namespace LibHac.FsSrv
|
namespace LibHac.FsSrv
|
||||||
{
|
{
|
||||||
@ -117,7 +117,7 @@ namespace LibHac.FsSrv
|
|||||||
using var scopedContext = new ScopedStorageLayoutTypeSetter(storageFlag);
|
using var scopedContext = new ScopedStorageLayoutTypeSetter(storageFlag);
|
||||||
|
|
||||||
// Normalize the path
|
// Normalize the path
|
||||||
var pathNormalized = new Fs.Path();
|
var pathNormalized = new Path();
|
||||||
rc = pathNormalized.Initialize(rootPath.Str);
|
rc = pathNormalized.Initialize(rootPath.Str);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
@ -135,7 +135,7 @@ namespace LibHac.FsSrv
|
|||||||
rc = _serviceImpl.OpenBisFileSystem(out baseFileSystem, partitionId, false);
|
rc = _serviceImpl.OpenBisFileSystem(out baseFileSystem, partitionId, false);
|
||||||
if (rc.IsFailure()) return rc;
|
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;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
// Add all the file system wrappers
|
// Add all the file system wrappers
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using LibHac.Common;
|
|
||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
using LibHac.FsSrv.FsCreator;
|
using LibHac.FsSrv.FsCreator;
|
||||||
using LibHac.FsSrv.Impl;
|
using LibHac.FsSrv.Impl;
|
||||||
|
@ -77,6 +77,11 @@ namespace LibHac.FsSrv.FsCreator
|
|||||||
Result rc = bisRootPath.Initialize(GetPartitionPath(partitionId).ToU8String());
|
Result rc = bisRootPath.Initialize(GetPartitionPath(partitionId).ToU8String());
|
||||||
if (rc.IsFailure()) return rc;
|
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> partitionFileSystem = null;
|
||||||
ReferenceCountedDisposable<IFileSystem> sharedRootFs = null;
|
ReferenceCountedDisposable<IFileSystem> sharedRootFs = null;
|
||||||
try
|
try
|
||||||
|
@ -83,7 +83,7 @@ namespace LibHac.FsSrv.FsCreator
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
tempFs = _rootFileSystem.AddReference();
|
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;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
outFileSystem = _sdCardFileSystem.AddReference();
|
outFileSystem = _sdCardFileSystem.AddReference();
|
||||||
|
@ -1,13 +1,10 @@
|
|||||||
using System;
|
using LibHac.Fs;
|
||||||
using LibHac.Fs;
|
|
||||||
using LibHac.Fs.Fsa;
|
using LibHac.Fs.Fsa;
|
||||||
|
|
||||||
namespace LibHac.FsSrv.FsCreator
|
namespace LibHac.FsSrv.FsCreator
|
||||||
{
|
{
|
||||||
public interface ITargetManagerFileSystemCreator
|
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 Create(out ReferenceCountedDisposable<IFileSystem> fileSystem, in Path rootPath, bool openCaseSensitive, bool ensureRootPathExists, Result pathNotFoundResult);
|
||||||
Result NormalizeCaseOfPath(out bool isSupported, ref Path path);
|
Result NormalizeCaseOfPath(out bool isSupported, ref Path path);
|
||||||
}
|
}
|
||||||
|
@ -6,11 +6,6 @@ namespace LibHac.FsSrv.FsCreator
|
|||||||
{
|
{
|
||||||
public class TargetManagerFileSystemCreator : ITargetManagerFileSystemCreator
|
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)
|
public Result Create(out ReferenceCountedDisposable<IFileSystem> fileSystem, in Path rootPath, bool openCaseSensitive, bool ensureRootPathExists, Result pathNotFoundResult)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
|
@ -15,15 +15,12 @@ namespace LibHac.FsSrv.Impl
|
|||||||
BaseFile = baseFile;
|
BaseFile = baseFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool disposing)
|
public override void Dispose()
|
||||||
{
|
|
||||||
if (disposing)
|
|
||||||
{
|
{
|
||||||
BaseFile?.Dispose();
|
BaseFile?.Dispose();
|
||||||
BaseFile = null;
|
BaseFile = null;
|
||||||
}
|
|
||||||
|
|
||||||
base.Dispose(disposing);
|
base.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Result DoRead(out long bytesRead, long offset, Span<byte> destination, in ReadOption option)
|
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;
|
BaseDirectory = baseDirectory;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool disposing)
|
public override void Dispose()
|
||||||
{
|
|
||||||
if (disposing)
|
|
||||||
{
|
{
|
||||||
BaseDirectory?.Dispose();
|
BaseDirectory?.Dispose();
|
||||||
BaseDirectory = null;
|
BaseDirectory = null;
|
||||||
}
|
|
||||||
|
|
||||||
base.Dispose(disposing);
|
base.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Result DoRead(out long entriesRead, Span<DirectoryEntry> entryBuffer)
|
protected override Result DoRead(out long entriesRead, Span<DirectoryEntry> entryBuffer)
|
||||||
|
@ -8,13 +8,11 @@ using LibHac.FsSystem;
|
|||||||
using LibHac.Lr;
|
using LibHac.Lr;
|
||||||
using LibHac.Ncm;
|
using LibHac.Ncm;
|
||||||
using LibHac.Spl;
|
using LibHac.Spl;
|
||||||
using LibHac.Util;
|
|
||||||
using IFileSystem = LibHac.Fs.Fsa.IFileSystem;
|
using IFileSystem = LibHac.Fs.Fsa.IFileSystem;
|
||||||
using IStorage = LibHac.Fs.IStorage;
|
using IStorage = LibHac.Fs.IStorage;
|
||||||
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
|
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
|
||||||
using IStorageSf = LibHac.FsSrv.Sf.IStorage;
|
using IStorageSf = LibHac.FsSrv.Sf.IStorage;
|
||||||
using Path = LibHac.Fs.Path;
|
using Path = LibHac.Fs.Path;
|
||||||
using PathNormalizer = LibHac.FsSrv.Impl.PathNormalizer;
|
|
||||||
using Utility = LibHac.FsSrv.Impl.Utility;
|
using Utility = LibHac.FsSrv.Impl.Utility;
|
||||||
|
|
||||||
namespace LibHac.FsSrv
|
namespace LibHac.FsSrv
|
||||||
@ -96,7 +94,7 @@ namespace LibHac.FsSrv
|
|||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
// Try to find the path to the original version of the file system
|
// 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,
|
Result originalResult = ServiceImpl.ResolveApplicationHtmlDocumentPath(out bool isDirectory,
|
||||||
ref originalPath, new Ncm.ApplicationId(programId.Value), ownerProgramInfo.StorageId);
|
ref originalPath, new Ncm.ApplicationId(programId.Value), ownerProgramInfo.StorageId);
|
||||||
|
|
||||||
@ -105,7 +103,7 @@ namespace LibHac.FsSrv
|
|||||||
return originalResult;
|
return originalResult;
|
||||||
|
|
||||||
// Try to find the path to the patch file system
|
// 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);
|
Result patchResult = ServiceImpl.ResolveRegisteredHtmlDocumentPath(ref patchPath, programId.Value);
|
||||||
|
|
||||||
ReferenceCountedDisposable<IFileSystem> tempFileSystem = null;
|
ReferenceCountedDisposable<IFileSystem> tempFileSystem = null;
|
||||||
@ -128,11 +126,11 @@ namespace LibHac.FsSrv
|
|||||||
if (patchResult.IsFailure())
|
if (patchResult.IsFailure())
|
||||||
return patchResult;
|
return patchResult;
|
||||||
|
|
||||||
var emptyPath = new Fs.Path();
|
var emptyPath = new Path();
|
||||||
rc = emptyPath.InitializeAsEmpty();
|
rc = emptyPath.InitializeAsEmpty();
|
||||||
if (rc.IsFailure()) return rc;
|
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
|
// Open the file system using both the original and patch versions
|
||||||
rc = ServiceImpl.OpenFileSystemWithPatch(out tempFileSystem, in originalNcaPath, in patchPath,
|
rc = ServiceImpl.OpenFileSystemWithPatch(out tempFileSystem, in originalNcaPath, in patchPath,
|
||||||
@ -235,7 +233,7 @@ namespace LibHac.FsSrv
|
|||||||
|
|
||||||
bool canMountSystemDataPrivate = ac.GetAccessibilityFor(AccessibilityType.MountSystemDataPrivate).CanRead;
|
bool canMountSystemDataPrivate = ac.GetAccessibilityFor(AccessibilityType.MountSystemDataPrivate).CanRead;
|
||||||
|
|
||||||
var pathNormalized = new Fs.Path();
|
var pathNormalized = new Path();
|
||||||
rc = pathNormalized.InitializeWithReplaceUnc(path.Str);
|
rc = pathNormalized.InitializeWithReplaceUnc(path.Str);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
@ -17,7 +17,6 @@ using IFile = LibHac.Fs.Fsa.IFile;
|
|||||||
using IFileSf = LibHac.FsSrv.Sf.IFile;
|
using IFileSf = LibHac.FsSrv.Sf.IFile;
|
||||||
using Path = LibHac.Fs.Path;
|
using Path = LibHac.Fs.Path;
|
||||||
using SaveData = LibHac.Fs.SaveData;
|
using SaveData = LibHac.Fs.SaveData;
|
||||||
using Utility = LibHac.FsSystem.Utility;
|
|
||||||
using static LibHac.Fs.StringTraits;
|
using static LibHac.Fs.StringTraits;
|
||||||
|
|
||||||
namespace LibHac.FsSrv
|
namespace LibHac.FsSrv
|
||||||
@ -1801,7 +1800,7 @@ namespace LibHac.FsSrv
|
|||||||
saveDataId);
|
saveDataId);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
commitId = Impl.Utility.ConvertZeroCommitId(in extraData);
|
commitId = Utility.ConvertZeroCommitId(in extraData);
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2228,7 +2227,7 @@ namespace LibHac.FsSrv
|
|||||||
{
|
{
|
||||||
saveService = _selfReference.AddReference();
|
saveService = _selfReference.AddReference();
|
||||||
|
|
||||||
Result rc = Utility.MakeUniqueLockWithPin(out uniqueLock, _openEntryCountSemaphore,
|
Result rc = Utility12.MakeUniqueLockWithPin(out uniqueLock, _openEntryCountSemaphore,
|
||||||
ref saveService);
|
ref saveService);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
@ -2252,7 +2251,7 @@ namespace LibHac.FsSrv
|
|||||||
{
|
{
|
||||||
saveService = _selfReference.AddReference();
|
saveService = _selfReference.AddReference();
|
||||||
|
|
||||||
Result rc = Utility.MakeUniqueLockWithPin(out uniqueLock, _saveDataMountCountSemaphore,
|
Result rc = Utility12.MakeUniqueLockWithPin(out uniqueLock, _saveDataMountCountSemaphore,
|
||||||
ref saveService);
|
ref saveService);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
@ -218,7 +218,7 @@ namespace LibHac.FsSrv
|
|||||||
UnsafeHelpers.SkipParamInit(out fileSystem);
|
UnsafeHelpers.SkipParamInit(out fileSystem);
|
||||||
|
|
||||||
// Hack around error CS8350.
|
// Hack around error CS8350.
|
||||||
const int bufferLength = 0xF;
|
const int bufferLength = 0x1B;
|
||||||
Span<byte> buffer = stackalloc byte[bufferLength];
|
Span<byte> buffer = stackalloc byte[bufferLength];
|
||||||
ref byte bufferRef = ref MemoryMarshal.GetReference(buffer);
|
ref byte bufferRef = ref MemoryMarshal.GetReference(buffer);
|
||||||
Span<byte> saveDataMetaIdDirectoryNameBuffer = MemoryMarshal.CreateSpan(ref bufferRef, bufferLength);
|
Span<byte> saveDataMetaIdDirectoryNameBuffer = MemoryMarshal.CreateSpan(ref bufferRef, bufferLength);
|
||||||
|
@ -122,14 +122,13 @@ namespace LibHac.FsSystem
|
|||||||
return BaseStorage.SetSize(Alignment.AlignUp(size, 0x10));
|
return BaseStorage.SetSize(Alignment.AlignUp(size, 0x10));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool disposing)
|
public override void Dispose()
|
||||||
{
|
|
||||||
if (disposing)
|
|
||||||
{
|
{
|
||||||
BaseStorage.Flush();
|
BaseStorage.Flush();
|
||||||
BaseStorage.Dispose();
|
BaseStorage.Dispose();
|
||||||
BaseFile?.Dispose();
|
BaseFile?.Dispose();
|
||||||
}
|
|
||||||
|
base.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -57,16 +57,16 @@ namespace LibHac.FsSystem
|
|||||||
/// <param name="options">Flags to control how the file is created.
|
/// <param name="options">Flags to control how the file is created.
|
||||||
/// Should usually be <see cref="CreateFileOptions.None"/></param>
|
/// 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>
|
/// <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);
|
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;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
var header = new AesXtsFileHeader(key, size, path.ToString(), KekSource, ValidationKey);
|
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;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
using (baseFile)
|
using (baseFile)
|
||||||
@ -105,7 +105,7 @@ namespace LibHac.FsSystem
|
|||||||
Result rc = BaseFileSystem.OpenDirectory(out IDirectory baseDir, path, mode);
|
Result rc = BaseFileSystem.OpenDirectory(out IDirectory baseDir, path, mode);
|
||||||
if (rc.IsFailure()) return rc;
|
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;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,7 +116,8 @@ namespace LibHac.FsSystem
|
|||||||
Result rc = BaseFileSystem.OpenFile(out IFile baseFile, path, mode | OpenMode.Read);
|
Result rc = BaseFileSystem.OpenFile(out IFile baseFile, path, mode | OpenMode.Read);
|
||||||
if (rc.IsFailure()) return rc;
|
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;
|
file = xtsFile;
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
|
@ -46,19 +46,18 @@ namespace LibHac.FsSystem
|
|||||||
_path = new Path.Stored();
|
_path = new Path.Stored();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool disposing)
|
public override void Dispose()
|
||||||
{
|
{
|
||||||
_path.Dispose();
|
_path.Dispose();
|
||||||
|
|
||||||
if (disposing)
|
|
||||||
{
|
|
||||||
foreach (IFile file in _files)
|
foreach (IFile file in _files)
|
||||||
{
|
{
|
||||||
file?.Dispose();
|
file?.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
_files.Clear();
|
_files.Clear();
|
||||||
}
|
|
||||||
|
base.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result Initialize(in Path path)
|
public Result Initialize(in Path path)
|
||||||
@ -391,15 +390,12 @@ namespace LibHac.FsSystem
|
|||||||
_concatenationFileSystem = concatFileSystem;
|
_concatenationFileSystem = concatFileSystem;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool disposing)
|
public override void Dispose()
|
||||||
{
|
|
||||||
if (disposing)
|
|
||||||
{
|
{
|
||||||
_path.Dispose();
|
_path.Dispose();
|
||||||
_baseDirectory.Dispose();
|
_baseDirectory.Dispose();
|
||||||
}
|
|
||||||
|
|
||||||
base.Dispose(disposing);
|
base.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result Initialize(in Path path)
|
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;
|
||||||
using LibHac.Fs.Fsa;
|
using LibHac.Fs.Fsa;
|
||||||
using LibHac.Util;
|
using LibHac.Util;
|
||||||
|
using Path = LibHac.Fs.Path;
|
||||||
|
|
||||||
namespace LibHac.FsSystem
|
namespace LibHac.FsSystem
|
||||||
{
|
{
|
||||||
@ -15,38 +16,119 @@ namespace LibHac.FsSystem
|
|||||||
public static Result CopyDirectory(this IFileSystem sourceFs, IFileSystem destFs, string sourcePath, string destPath,
|
public static Result CopyDirectory(this IFileSystem sourceFs, IFileSystem destFs, string sourcePath, string destPath,
|
||||||
IProgressReport logger = null, CreateFileOptions options = CreateFileOptions.None)
|
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();
|
||||||
{
|
|
||||||
string subSrcPath = PathTools.Normalize(PathTools.Combine(sourcePath, entry.Name));
|
|
||||||
string subDstPath = PathTools.Normalize(PathTools.Combine(destPath, entry.Name));
|
|
||||||
|
|
||||||
if (entry.Type == DirectoryEntryType.Directory)
|
var sourcePathNormalized = new Path();
|
||||||
{
|
Result rc = InitializeFromString(ref sourcePathNormalized, sourcePath);
|
||||||
destFs.EnsureDirectoryExists(subDstPath);
|
|
||||||
|
|
||||||
rc = sourceFs.CopyDirectory(destFs, subSrcPath, subDstPath, logger, options);
|
|
||||||
if (rc.IsFailure()) return rc;
|
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
|
||||||
|
{
|
||||||
|
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.File)
|
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)
|
||||||
{
|
{
|
||||||
destFs.CreateOrOverwriteFile(subDstPath, entry.Size, options);
|
static Result OnEnterDir(in Path path, in DirectoryEntry entry,
|
||||||
|
ref Utility12.FsIterationTaskClosure closure)
|
||||||
rc = sourceFs.OpenFile(out IFile srcFile, subSrcPath.ToU8Span(), OpenMode.Read);
|
{
|
||||||
|
Result rc = closure.DestinationPathBuffer.AppendChild(entry.Name);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
using (srcFile)
|
return closure.SourceFileSystem.CreateDirectory(in closure.DestinationPathBuffer);
|
||||||
{
|
|
||||||
rc = destFs.OpenFile(out IFile dstFile, subDstPath.ToU8Span(), OpenMode.Write | OpenMode.AllowAppend);
|
|
||||||
if (rc.IsFailure()) return rc;
|
|
||||||
|
|
||||||
using (dstFile)
|
|
||||||
{
|
|
||||||
logger?.LogMessage(subSrcPath);
|
|
||||||
srcFile.CopyTo(dstFile, logger);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
// Read/Write file in work buffer sized chunks.
|
||||||
|
long remaining = fileSize;
|
||||||
|
long offset = 0;
|
||||||
|
|
||||||
|
logger?.SetTotal(fileSize);
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
logger?.ReportAdd(bytesRead);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -81,9 +163,10 @@ namespace LibHac.FsSystem
|
|||||||
bool ignoreCase = searchOptions.HasFlag(SearchOptions.CaseInsensitive);
|
bool ignoreCase = searchOptions.HasFlag(SearchOptions.CaseInsensitive);
|
||||||
bool recurse = searchOptions.HasFlag(SearchOptions.RecurseSubdirectories);
|
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)
|
while (true)
|
||||||
{
|
{
|
||||||
@ -102,7 +185,7 @@ namespace LibHac.FsSystem
|
|||||||
if (entry.Type != DirectoryEntryType.Directory || !recurse) continue;
|
if (entry.Type != DirectoryEntryType.Directory || !recurse) continue;
|
||||||
|
|
||||||
IEnumerable<DirectoryEntryEx> subEntries =
|
IEnumerable<DirectoryEntryEx> subEntries =
|
||||||
fs.EnumerateEntries(PathTools.Combine(path, entry.Name), searchPattern,
|
fileSystem.EnumerateEntries(PathTools.Combine(path, entry.Name), searchPattern,
|
||||||
searchOptions);
|
searchOptions);
|
||||||
|
|
||||||
foreach (DirectoryEntryEx subEntry in subEntries)
|
foreach (DirectoryEntryEx subEntry in subEntries)
|
||||||
@ -202,7 +285,10 @@ namespace LibHac.FsSystem
|
|||||||
|
|
||||||
public static void SetConcatenationFileAttribute(this IFileSystem fs, string path)
|
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)
|
public static void CleanDirectoryRecursivelyGeneric(IFileSystem fileSystem, string path)
|
||||||
@ -213,14 +299,17 @@ namespace LibHac.FsSystem
|
|||||||
{
|
{
|
||||||
string subPath = PathTools.Combine(path, entry.Name);
|
string subPath = PathTools.Combine(path, entry.Name);
|
||||||
|
|
||||||
|
var subPathNormalized = new Path();
|
||||||
|
InitializeFromString(ref subPathNormalized, subPath).ThrowIfFailure();
|
||||||
|
|
||||||
if (entry.Type == DirectoryEntryType.Directory)
|
if (entry.Type == DirectoryEntryType.Directory)
|
||||||
{
|
{
|
||||||
CleanDirectoryRecursivelyGeneric(fileSystem, subPath);
|
CleanDirectoryRecursivelyGeneric(fileSystem, subPath);
|
||||||
fs.DeleteDirectory(subPath.ToU8Span());
|
fs.DeleteDirectory(in subPathNormalized);
|
||||||
}
|
}
|
||||||
else if (entry.Type == DirectoryEntryType.File)
|
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)
|
public static Result EnsureDirectoryExists(this IFileSystem fs, string path)
|
||||||
{
|
{
|
||||||
path = PathTools.Normalize(path);
|
var pathNormalized = new Path();
|
||||||
if (fs.DirectoryExists(path)) return Result.Success;
|
Result rc = InitializeFromString(ref pathNormalized, path);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
// Find the first subdirectory in the chain that doesn't exist
|
return Utility12.EnsureDirectory(fs, in pathNormalized);
|
||||||
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
|
public static Result CreateOrOverwriteFile(IFileSystem fileSystem, in Path path, long size,
|
||||||
i++;
|
CreateFileOptions option = CreateFileOptions.None)
|
||||||
|
|
||||||
// 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] == '/')
|
Result rc = fileSystem.CreateFile(in path, size, option);
|
||||||
{
|
|
||||||
string subPath = path.Substring(0, i);
|
|
||||||
|
|
||||||
Result rc = fs.CreateDirectory(subPath.ToU8Span());
|
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;
|
if (rc.IsFailure()) return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
return fs.CreateDirectory(path.ToU8Span());
|
private static Result InitializeFromString(ref Path outPath, string path)
|
||||||
}
|
|
||||||
|
|
||||||
public static void CreateOrOverwriteFile(this IFileSystem fs, string path, long size)
|
|
||||||
{
|
{
|
||||||
fs.CreateOrOverwriteFile(path, size, CreateFileOptions.None);
|
ReadOnlySpan<byte> utf8Path = StringUtils.StringToUtf8(path);
|
||||||
}
|
|
||||||
|
|
||||||
public static void CreateOrOverwriteFile(this IFileSystem fs, string path, long size, CreateFileOptions options)
|
Result rc = outPath.Initialize(utf8Path);
|
||||||
{
|
if (rc.IsFailure()) return rc;
|
||||||
path = PathTools.Normalize(path);
|
|
||||||
|
|
||||||
if (fs.FileExists(path)) fs.DeleteFile(path.ToU8Span());
|
var pathFlags = new PathFlags();
|
||||||
|
pathFlags.AllowEmptyPath();
|
||||||
|
outPath.Normalize(pathFlags);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
fs.CreateFile(path.ToU8Span(), size, CreateFileOptions.None);
|
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)
|
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())
|
if (rc.IsSuccess())
|
||||||
{
|
{
|
||||||
@ -82,8 +82,8 @@ namespace LibHac.FsSystem
|
|||||||
|
|
||||||
if (!(multipleSources is null))
|
if (!(multipleSources is null))
|
||||||
{
|
{
|
||||||
var dir = new MergedDirectory(multipleSources, path, mode);
|
var dir = new MergedDirectory(multipleSources, mode);
|
||||||
Result rc = dir.Initialize();
|
Result rc = dir.Initialize(in path);
|
||||||
|
|
||||||
if (rc.IsSuccess())
|
if (rc.IsSuccess())
|
||||||
{
|
{
|
||||||
@ -207,25 +207,27 @@ namespace LibHac.FsSystem
|
|||||||
// Needed to open new directories for GetEntryCount
|
// Needed to open new directories for GetEntryCount
|
||||||
private List<IFileSystem> SourceFileSystems { get; }
|
private List<IFileSystem> SourceFileSystems { get; }
|
||||||
private List<IDirectory> SourceDirs { get; }
|
private List<IDirectory> SourceDirs { get; }
|
||||||
private U8String Path { get; }
|
private Path.Stored _path;
|
||||||
private OpenDirectoryMode Mode { get; }
|
private OpenDirectoryMode Mode { get; }
|
||||||
|
|
||||||
// todo: Efficient way to remove duplicates
|
// todo: Efficient way to remove duplicates
|
||||||
private HashSet<string> Names { get; } = new HashSet<string>();
|
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;
|
SourceFileSystems = sourceFileSystems;
|
||||||
SourceDirs = new List<IDirectory>(sourceFileSystems.Count);
|
SourceDirs = new List<IDirectory>(sourceFileSystems.Count);
|
||||||
Path = path.ToU8String();
|
|
||||||
Mode = mode;
|
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)
|
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;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
SourceDirs.Add(dir);
|
SourceDirs.Add(dir);
|
||||||
@ -268,10 +270,12 @@ namespace LibHac.FsSystem
|
|||||||
// todo: Efficient way to remove duplicates
|
// todo: Efficient way to remove duplicates
|
||||||
var names = new HashSet<string>();
|
var names = new HashSet<string>();
|
||||||
|
|
||||||
|
Path path = _path.GetPath();
|
||||||
|
|
||||||
// Open new directories for each source because we need to remove duplicate entries
|
// Open new directories for each source because we need to remove duplicate entries
|
||||||
foreach (IFileSystem fs in SourceFileSystems)
|
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;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
long entriesRead;
|
long entriesRead;
|
||||||
|
@ -92,14 +92,12 @@ namespace LibHac.FsSystem
|
|||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool disposing)
|
public override void Dispose()
|
||||||
{
|
|
||||||
if (disposing)
|
|
||||||
{
|
{
|
||||||
File?.Dispose();
|
File?.Dispose();
|
||||||
}
|
|
||||||
|
|
||||||
Stream?.Dispose();
|
Stream?.Dispose();
|
||||||
|
|
||||||
|
base.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Text.Unicode;
|
using System.Text.Unicode;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
@ -33,7 +32,8 @@ namespace LibHac.FsSystem
|
|||||||
CaseSensitive
|
CaseSensitive
|
||||||
}
|
}
|
||||||
|
|
||||||
private string _rootPath;
|
private Path.Stored _rootPath;
|
||||||
|
private string _rootPathUtf16;
|
||||||
private readonly FileSystemClient _fsClient;
|
private readonly FileSystemClient _fsClient;
|
||||||
private PathMode _mode;
|
private PathMode _mode;
|
||||||
private readonly bool _useUnixTime;
|
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>
|
/// <param name="rootPath">The path that will be the root of the <see cref="LocalFileSystem"/>.</param>
|
||||||
public LocalFileSystem(string rootPath)
|
public LocalFileSystem(string rootPath)
|
||||||
{
|
{
|
||||||
_rootPath = System.IO.Path.GetFullPath(rootPath);
|
Result rc = Initialize(rootPath, PathMode.DefaultCaseSensitivity, true);
|
||||||
|
if (rc.IsFailure())
|
||||||
if (!Directory.Exists(_rootPath))
|
throw new HorizonResultException(rc, "Error creating LocalFileSystem.");
|
||||||
{
|
|
||||||
if (File.Exists(_rootPath))
|
|
||||||
{
|
|
||||||
throw new DirectoryNotFoundException($"The specified path is a file. ({rootPath})");
|
|
||||||
}
|
|
||||||
|
|
||||||
Directory.CreateDirectory(_rootPath);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Result Create(out LocalFileSystem fileSystem, string rootPath,
|
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)
|
public Result Initialize(string rootPath, PathMode pathMode, bool ensurePathExists)
|
||||||
{
|
{
|
||||||
|
Result rc;
|
||||||
|
|
||||||
if (rootPath == null)
|
if (rootPath == null)
|
||||||
return ResultFs.NullptrArgument.Log();
|
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 the root path is empty, we interpret any incoming paths as rooted paths.
|
||||||
if (rootPath == string.Empty)
|
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;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string rootPathNormalized;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_rootPath = System.IO.Path.GetFullPath(rootPath);
|
rootPathNormalized = System.IO.Path.GetFullPath(rootPath);
|
||||||
}
|
}
|
||||||
catch (PathTooLongException)
|
catch (PathTooLongException)
|
||||||
{
|
{
|
||||||
@ -110,14 +112,14 @@ namespace LibHac.FsSystem
|
|||||||
return ResultFs.InvalidCharacter.Log();
|
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();
|
return ResultFs.PathNotFound.Log();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Directory.CreateDirectory(_rootPath);
|
Directory.CreateDirectory(rootPathNormalized);
|
||||||
}
|
}
|
||||||
catch (Exception ex) when (ex.HResult < 0)
|
catch (Exception ex) when (ex.HResult < 0)
|
||||||
{
|
{
|
||||||
@ -125,53 +127,78 @@ namespace LibHac.FsSystem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ReadOnlySpan<byte> utf8Path = StringUtils.StringToUtf8(rootPathNormalized);
|
||||||
|
var pathNormalized = new Path();
|
||||||
|
|
||||||
|
if (utf8Path.At(0) == DirectorySeparator && utf8Path.At(1) != DirectorySeparator)
|
||||||
|
{
|
||||||
|
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;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Result ResolveFullPath(out string fullPath, U8Span path, bool checkCaseSensitivity)
|
private Result ResolveFullPath(out string outFullPath, in Path path, bool checkCaseSensitivity)
|
||||||
{
|
{
|
||||||
UnsafeHelpers.SkipParamInit(out fullPath);
|
UnsafeHelpers.SkipParamInit(out outFullPath);
|
||||||
|
|
||||||
Unsafe.SkipInit(out FsPath normalizedPath);
|
// 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.
|
||||||
|
|
||||||
Result rc = PathNormalizer.Normalize(normalizedPath.Str, out _, path, false, false);
|
var pathNormalized = new Path();
|
||||||
|
Result rc = pathNormalized.Initialize(path.GetString());
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
fullPath = PathTools.Combine(_rootPath, normalizedPath.ToString());
|
var pathFlags = new PathFlags();
|
||||||
|
pathFlags.AllowWindowsPath();
|
||||||
|
pathFlags.AllowRelativePath();
|
||||||
|
pathFlags.AllowEmptyPath();
|
||||||
|
rc = pathNormalized.Normalize(pathFlags);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
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)
|
if (_mode == PathMode.CaseSensitive && checkCaseSensitivity)
|
||||||
{
|
{
|
||||||
rc = CheckPathCaseSensitively(fullPath);
|
rc = CheckPathCaseSensitively(utf16FullPath);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
outFullPath = utf16FullPath;
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Result CheckSubPath(U8Span path1, U8Span path2)
|
protected override Result DoGetFileAttributes(out NxFileAttributes attributes, in Path path)
|
||||||
{
|
|
||||||
Unsafe.SkipInit(out FsPath normalizedPath1);
|
|
||||||
Unsafe.SkipInit(out FsPath normalizedPath2);
|
|
||||||
|
|
||||||
Result rc = PathNormalizer.Normalize(normalizedPath1.Str, out _, path1, false, false);
|
|
||||||
if (rc.IsFailure()) return rc;
|
|
||||||
|
|
||||||
rc = PathNormalizer.Normalize(normalizedPath2.Str, out _, path2, false, false);
|
|
||||||
if (rc.IsFailure()) return rc;
|
|
||||||
|
|
||||||
if (PathUtility.IsSubPath(normalizedPath1, normalizedPath2))
|
|
||||||
{
|
|
||||||
return ResultFs.DirectoryNotRenamable.Log();
|
|
||||||
}
|
|
||||||
|
|
||||||
return Result.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override Result DoGetFileAttributes(out NxFileAttributes attributes, U8Span path)
|
|
||||||
{
|
{
|
||||||
UnsafeHelpers.SkipParamInit(out attributes);
|
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;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
rc = GetFileInfo(out FileInfo info, fullPath);
|
rc = GetFileInfo(out FileInfo info, fullPath);
|
||||||
@ -186,9 +213,9 @@ namespace LibHac.FsSystem
|
|||||||
return Result.Success;
|
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;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
rc = GetFileInfo(out FileInfo info, fullPath);
|
rc = GetFileInfo(out FileInfo info, fullPath);
|
||||||
@ -214,11 +241,11 @@ namespace LibHac.FsSystem
|
|||||||
return Result.Success;
|
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);
|
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;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
rc = GetFileInfo(out FileInfo info, fullPath);
|
rc = GetFileInfo(out FileInfo info, fullPath);
|
||||||
@ -232,9 +259,9 @@ namespace LibHac.FsSystem
|
|||||||
return DoCreateDirectory(path, NxFileAttributes.None);
|
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;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
rc = GetDirInfo(out DirectoryInfo dir, fullPath);
|
rc = GetDirInfo(out DirectoryInfo dir, fullPath);
|
||||||
@ -255,7 +282,7 @@ namespace LibHac.FsSystem
|
|||||||
|
|
||||||
protected override Result DoCreateFile(in Path path, long size, CreateFileOptions option)
|
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;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
rc = GetFileInfo(out FileInfo file, fullPath);
|
rc = GetFileInfo(out FileInfo file, fullPath);
|
||||||
@ -283,7 +310,7 @@ namespace LibHac.FsSystem
|
|||||||
|
|
||||||
protected override Result DoDeleteDirectory(in Path path)
|
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;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
rc = GetDirInfo(out DirectoryInfo dir, fullPath);
|
rc = GetDirInfo(out DirectoryInfo dir, fullPath);
|
||||||
@ -295,7 +322,7 @@ namespace LibHac.FsSystem
|
|||||||
|
|
||||||
protected override Result DoDeleteDirectoryRecursively(in Path path)
|
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;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
rc = GetDirInfo(out DirectoryInfo dir, fullPath);
|
rc = GetDirInfo(out DirectoryInfo dir, fullPath);
|
||||||
@ -307,7 +334,7 @@ namespace LibHac.FsSystem
|
|||||||
|
|
||||||
protected override Result DoCleanDirectoryRecursively(in Path path)
|
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;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
foreach (string file in Directory.EnumerateFiles(fullPath))
|
foreach (string file in Directory.EnumerateFiles(fullPath))
|
||||||
@ -343,7 +370,7 @@ namespace LibHac.FsSystem
|
|||||||
|
|
||||||
protected override Result DoDeleteFile(in Path path)
|
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;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
rc = GetFileInfo(out FileInfo file, fullPath);
|
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)
|
protected override Result DoOpenDirectory(out IDirectory directory, in Path path, OpenDirectoryMode mode)
|
||||||
{
|
{
|
||||||
UnsafeHelpers.SkipParamInit(out directory);
|
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;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
rc = GetDirInfo(out DirectoryInfo dirInfo, fullPath);
|
rc = GetDirInfo(out DirectoryInfo dirInfo, fullPath);
|
||||||
@ -380,7 +407,7 @@ namespace LibHac.FsSystem
|
|||||||
{
|
{
|
||||||
UnsafeHelpers.SkipParamInit(out file);
|
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;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
rc = GetEntryType(out DirectoryEntryType entryType, path);
|
rc = GetEntryType(out DirectoryEntryType entryType, path);
|
||||||
@ -403,13 +430,10 @@ namespace LibHac.FsSystem
|
|||||||
|
|
||||||
protected override Result DoRenameDirectory(in Path currentPath, in Path newPath)
|
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;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
rc = ResolveFullPath(out string fullCurrentPath, currentPath, true);
|
rc = ResolveFullPath(out string fullNewPath, in newPath, false);
|
||||||
if (rc.IsFailure()) return rc;
|
|
||||||
|
|
||||||
rc = ResolveFullPath(out string fullNewPath, newPath, false);
|
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
// Official FS behavior is to do nothing in this case
|
// 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)
|
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;
|
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;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
// Official FS behavior is to do nothing in this case
|
// Official FS behavior is to do nothing in this case
|
||||||
@ -450,7 +474,7 @@ namespace LibHac.FsSystem
|
|||||||
{
|
{
|
||||||
UnsafeHelpers.SkipParamInit(out entryType);
|
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;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
rc = GetDirInfo(out DirectoryInfo dir, fullPath);
|
rc = GetDirInfo(out DirectoryInfo dir, fullPath);
|
||||||
@ -478,7 +502,7 @@ namespace LibHac.FsSystem
|
|||||||
{
|
{
|
||||||
UnsafeHelpers.SkipParamInit(out timeStamp);
|
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;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
rc = GetFileInfo(out FileInfo file, fullPath);
|
rc = GetFileInfo(out FileInfo file, fullPath);
|
||||||
@ -508,7 +532,7 @@ namespace LibHac.FsSystem
|
|||||||
{
|
{
|
||||||
UnsafeHelpers.SkipParamInit(out freeSpace);
|
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;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
freeSpace = new DriveInfo(fullPath).AvailableFreeSpace;
|
freeSpace = new DriveInfo(fullPath).AvailableFreeSpace;
|
||||||
@ -519,7 +543,7 @@ namespace LibHac.FsSystem
|
|||||||
{
|
{
|
||||||
UnsafeHelpers.SkipParamInit(out totalSpace);
|
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;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
totalSpace = new DriveInfo(fullPath).TotalSize;
|
totalSpace = new DriveInfo(fullPath).TotalSize;
|
||||||
@ -802,7 +826,7 @@ namespace LibHac.FsSystem
|
|||||||
|
|
||||||
private Result CheckPathCaseSensitively(string path)
|
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 (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
if (path.Length != caseSensitivePath.Length)
|
if (path.Length != caseSensitivePath.Length)
|
||||||
|
@ -42,9 +42,9 @@ namespace LibHac.FsSystem
|
|||||||
|
|
||||||
protected override Result DoOpenFile(out IFile file, in Path path, OpenMode mode)
|
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);
|
ThrowHelper.ThrowResult(ResultFs.PathNotFound.Value);
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,7 @@ namespace LibHac.FsSystem
|
|||||||
|
|
||||||
ReadOnlySpan<byte> rootPath = new[] { (byte)'/' };
|
ReadOnlySpan<byte> rootPath = new[] { (byte)'/' };
|
||||||
|
|
||||||
if (StringUtils.Compare(rootPath, path, 2) != 0)
|
if (path == rootPath)
|
||||||
return ResultFs.PathNotFound.Log();
|
return ResultFs.PathNotFound.Log();
|
||||||
|
|
||||||
directory = new PartitionDirectory(this, mode);
|
directory = new PartitionDirectory(this, mode);
|
||||||
@ -76,7 +76,7 @@ namespace LibHac.FsSystem
|
|||||||
if (!mode.HasFlag(OpenMode.Read) && !mode.HasFlag(OpenMode.Write))
|
if (!mode.HasFlag(OpenMode.Read) && !mode.HasFlag(OpenMode.Write))
|
||||||
return ResultFs.InvalidArgument.Log();
|
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();
|
if (entryIndex < 0) return ResultFs.PathNotFound.Log();
|
||||||
|
|
||||||
ref T entry = ref MetaData.GetEntry(entryIndex);
|
ref T entry = ref MetaData.GetEntry(entryIndex);
|
||||||
@ -93,18 +93,20 @@ namespace LibHac.FsSystem
|
|||||||
if (!IsInitialized)
|
if (!IsInitialized)
|
||||||
return ResultFs.PreconditionViolation.Log();
|
return ResultFs.PreconditionViolation.Log();
|
||||||
|
|
||||||
if (path.IsEmpty() || path[0] != '/')
|
ReadOnlySpan<byte> pathStr = path.GetString();
|
||||||
|
|
||||||
|
if (path.IsEmpty() || pathStr[0] != '/')
|
||||||
return ResultFs.InvalidPathFormat.Log();
|
return ResultFs.InvalidPathFormat.Log();
|
||||||
|
|
||||||
ReadOnlySpan<byte> rootPath = new[] { (byte)'/' };
|
ReadOnlySpan<byte> rootPath = new[] { (byte)'/' };
|
||||||
|
|
||||||
if (StringUtils.Compare(rootPath, path, 2) == 0)
|
if (StringUtils.Compare(rootPath, pathStr, 2) == 0)
|
||||||
{
|
{
|
||||||
entryType = DirectoryEntryType.Directory;
|
entryType = DirectoryEntryType.Directory;
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (MetaData.FindEntry(path.Slice(1)) >= 0)
|
if (MetaData.FindEntry(new U8Span(pathStr.Slice(1))) >= 0)
|
||||||
{
|
{
|
||||||
entryType = DirectoryEntryType.File;
|
entryType = DirectoryEntryType.File;
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
|
@ -3,6 +3,7 @@ using System.Diagnostics;
|
|||||||
using System.IO.Enumeration;
|
using System.IO.Enumeration;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
|
using LibHac.Diag;
|
||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
using LibHac.Util;
|
using LibHac.Util;
|
||||||
|
|
||||||
@ -235,7 +236,7 @@ namespace LibHac.FsSystem
|
|||||||
|
|
||||||
public static ReadOnlySpan<byte> GetParentDirectory(ReadOnlySpan<byte> path)
|
public static ReadOnlySpan<byte> GetParentDirectory(ReadOnlySpan<byte> path)
|
||||||
{
|
{
|
||||||
Debug.Assert(IsNormalized(path));
|
Assert.SdkAssert(IsNormalized(path));
|
||||||
|
|
||||||
int i = StringUtils.GetLength(path) - 1;
|
int i = StringUtils.GetLength(path) - 1;
|
||||||
|
|
||||||
@ -288,6 +289,9 @@ namespace LibHac.FsSystem
|
|||||||
|
|
||||||
foreach (char c in path)
|
foreach (char c in path)
|
||||||
{
|
{
|
||||||
|
if (c == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
switch (state)
|
switch (state)
|
||||||
{
|
{
|
||||||
case NormalizeState.Initial when c == '/': state = NormalizeState.Delimiter; break;
|
case NormalizeState.Initial when c == '/': state = NormalizeState.Delimiter; break;
|
||||||
@ -325,6 +329,9 @@ namespace LibHac.FsSystem
|
|||||||
|
|
||||||
foreach (byte c in path)
|
foreach (byte c in path)
|
||||||
{
|
{
|
||||||
|
if (c == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
switch (state)
|
switch (state)
|
||||||
{
|
{
|
||||||
case NormalizeState.Initial when c == '/': state = NormalizeState.Delimiter; break;
|
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;
|
UniqueLock<SemaphoreAdapter> tempUniqueLock = default;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
tempUniqueLock = new UniqueLock<SemaphoreAdapter>(semaphore);
|
tempUniqueLock = new UniqueLock<SemaphoreAdapter>(semaphore, new DeferLock());
|
||||||
|
|
||||||
if (!tempUniqueLock.TryLock())
|
if (!tempUniqueLock.TryLock())
|
||||||
{
|
{
|
||||||
|
@ -8,7 +8,7 @@ namespace LibHac.Lr
|
|||||||
internal struct LrServiceGlobals
|
internal struct LrServiceGlobals
|
||||||
{
|
{
|
||||||
public ILocationResolverManager LocationResolver;
|
public ILocationResolverManager LocationResolver;
|
||||||
public SdkMutex InitializationMutex;
|
public SdkMutexType InitializationMutex;
|
||||||
|
|
||||||
public void Initialize()
|
public void Initialize()
|
||||||
{
|
{
|
||||||
@ -34,7 +34,7 @@ namespace LibHac.Lr
|
|||||||
Assert.SdkRequiresNotNull(globals.LocationResolver);
|
Assert.SdkRequiresNotNull(globals.LocationResolver);
|
||||||
|
|
||||||
// The lock over getting the service object is a LibHac addition.
|
// 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)
|
if (globals.LocationResolver is not null)
|
||||||
return;
|
return;
|
||||||
|
@ -5,6 +5,12 @@ using LibHac.Common;
|
|||||||
|
|
||||||
namespace LibHac.Os
|
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
|
public static class UniqueLock
|
||||||
{
|
{
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
@ -33,6 +39,13 @@ namespace LibHac.Os
|
|||||||
_ownsLock = true;
|
_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)
|
public UniqueLockRef(ref UniqueLockRef<TMutex> other)
|
||||||
{
|
{
|
||||||
this = other;
|
this = other;
|
||||||
@ -99,11 +112,18 @@ namespace LibHac.Os
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public UniqueLock(TMutex mutex)
|
public UniqueLock(TMutex mutex)
|
||||||
{
|
{
|
||||||
_mutex = new Ref<TMutex>(ref mutex);
|
_mutex = mutex;
|
||||||
mutex.Lock();
|
mutex.Lock();
|
||||||
_ownsLock = true;
|
_ownsLock = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public UniqueLock(TMutex mutex, DeferLock tag)
|
||||||
|
{
|
||||||
|
_mutex = mutex;
|
||||||
|
_ownsLock = false;
|
||||||
|
}
|
||||||
|
|
||||||
public UniqueLock(ref UniqueLock<TMutex> other)
|
public UniqueLock(ref UniqueLock<TMutex> other)
|
||||||
{
|
{
|
||||||
this = other;
|
this = other;
|
||||||
|
@ -44,14 +44,20 @@ namespace LibHac
|
|||||||
{
|
{
|
||||||
var concatFs = new ConcatenationFileSystem(fileSystem);
|
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);
|
var contentDirFs = new SubdirectoryFileSystem(concatFs);
|
||||||
contentDirFs.Initialize("/Nintendo/Contents".ToU8String()).ThrowIfFailure();
|
contentDirFs.Initialize(in contentDirPath).ThrowIfFailure();
|
||||||
|
|
||||||
AesXtsFileSystem encSaveFs = null;
|
AesXtsFileSystem encSaveFs = null;
|
||||||
if (fileSystem.DirectoryExists("/Nintendo/save"))
|
if (fileSystem.DirectoryExists("/Nintendo/save"))
|
||||||
{
|
{
|
||||||
var saveDirFs = new SubdirectoryFileSystem(concatFs);
|
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);
|
encSaveFs = new AesXtsFileSystem(saveDirFs, keySet.SdCardEncryptionKeys[0].DataRo.ToArray(), 0x4000);
|
||||||
}
|
}
|
||||||
@ -65,7 +71,7 @@ namespace LibHac
|
|||||||
{
|
{
|
||||||
var concatFs = new ConcatenationFileSystem(fileSystem);
|
var concatFs = new ConcatenationFileSystem(fileSystem);
|
||||||
SubdirectoryFileSystem saveDirFs = null;
|
SubdirectoryFileSystem saveDirFs = null;
|
||||||
SubdirectoryFileSystem contentDirFs = null;
|
SubdirectoryFileSystem contentDirFs;
|
||||||
|
|
||||||
if (concatFs.DirectoryExists("/save"))
|
if (concatFs.DirectoryExists("/save"))
|
||||||
{
|
{
|
||||||
|
@ -15,7 +15,7 @@ namespace hactoolnet
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var localFs = new LocalFileSystem(ctx.Options.InFile);
|
LocalFileSystem.Create(out LocalFileSystem localFs, ctx.Options.InFile).ThrowIfFailure();
|
||||||
|
|
||||||
var builder = new RomFsBuilder(localFs);
|
var builder = new RomFsBuilder(localFs);
|
||||||
IStorage romFs = builder.Build();
|
IStorage romFs = builder.Build();
|
||||||
|
@ -43,8 +43,8 @@ namespace LibHac.Tests.Fs.FileSystemClientTests.ShimTests
|
|||||||
var applicationId = new Ncm.ApplicationId(1);
|
var applicationId = new Ncm.ApplicationId(1);
|
||||||
FileSystemClient fs = FileSystemServerFactory.CreateClient(true);
|
FileSystemClient fs = FileSystemServerFactory.CreateClient(true);
|
||||||
|
|
||||||
fs.CreateCacheStorage(applicationId, SaveDataSpaceId.SdCache, applicationId.Value, 0, 0, SaveDataFlags.None);
|
Assert.Success(fs.CreateCacheStorage(applicationId, SaveDataSpaceId.SdCache, applicationId.Value, 0, 0, SaveDataFlags.None));
|
||||||
fs.MountCacheStorage("cache".ToU8Span(), applicationId);
|
Assert.Success(fs.MountCacheStorage("cache".ToU8Span(), applicationId));
|
||||||
fs.CreateFile("cache:/sd".ToU8Span(), 0);
|
fs.CreateFile("cache:/sd".ToU8Span(), 0);
|
||||||
fs.Commit("cache".ToU8Span());
|
fs.Commit("cache".ToU8Span());
|
||||||
fs.Unmount("cache".ToU8Span());
|
fs.Unmount("cache".ToU8Span());
|
||||||
|
@ -1,13 +1,7 @@
|
|||||||
using System;
|
namespace LibHac.Tests.FsSystem
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace LibHac.Tests.FsSystem
|
|
||||||
{
|
{
|
||||||
class ConcatenationFileSystemTests
|
public class ConcatenationFileSystemTests
|
||||||
{
|
{
|
||||||
asdf
|
//asdf
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using Xunit.Sdk;
|
using Xunit.Sdk;
|
||||||
|
|
||||||
|
// ReSharper disable once CheckNamespace
|
||||||
namespace Xunit
|
namespace Xunit
|
||||||
{
|
{
|
||||||
public partial class Assert
|
public partial class Assert
|
||||||
|
Loading…
x
Reference in New Issue
Block a user