mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Implement AccessLogSdCardWriter
This commit is contained in:
parent
86336f06ec
commit
d0c96e7b8e
1 changed files with 257 additions and 0 deletions
257
src/LibHac/FsSrv/Impl/AccessLogSdCardWriter.cs
Normal file
257
src/LibHac/FsSrv/Impl/AccessLogSdCardWriter.cs
Normal file
|
@ -0,0 +1,257 @@
|
||||||
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
|
using LibHac.Common;
|
||||||
|
using LibHac.Fs;
|
||||||
|
using LibHac.Fs.Fsa;
|
||||||
|
using LibHac.Fs.Shim;
|
||||||
|
using LibHac.Os;
|
||||||
|
|
||||||
|
namespace LibHac.FsSrv.Impl;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Manages creating and opening the filesystem access log file on the SD card, and writes messages to the log file.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Based on nnSdk 18.3.0 (FS 18.0.0)</remarks>
|
||||||
|
public class AccessLogSdCardWriter : IDisposable
|
||||||
|
{
|
||||||
|
private bool _isLogFileOpen;
|
||||||
|
private bool _isSetUp;
|
||||||
|
private FileHandle _fileHandle;
|
||||||
|
private byte[] _workBuffer;
|
||||||
|
private long _logFilePosition;
|
||||||
|
private int _bufferPosition;
|
||||||
|
private SdkMutex _mutex;
|
||||||
|
|
||||||
|
// Libhac addition
|
||||||
|
private FileSystemClient _fsClient;
|
||||||
|
|
||||||
|
private const int WorkBufferSize = 0x4000;
|
||||||
|
|
||||||
|
private ReadOnlySpan<byte> AccessLogMountName => "$FsAccessLog"u8;
|
||||||
|
private ReadOnlySpan<byte> AccessLogFilePath => "$FsAccessLog:/FsAccessLog.txt"u8;
|
||||||
|
|
||||||
|
private ReadOnlySpan<byte> BomUtf8 => [0xEF, 0xBB, 0xBF];
|
||||||
|
private ReadOnlySpan<byte> AccessLogStartMarker => "FS_ACCESS: { start_tag: true }\n"u8;
|
||||||
|
private ReadOnlySpan<byte> AccessLogEndMarker => "FS_ACCESS: { end_tag: true }\n"u8;
|
||||||
|
|
||||||
|
public AccessLogSdCardWriter(FileSystemClient fsClient)
|
||||||
|
{
|
||||||
|
_isLogFileOpen = false;
|
||||||
|
_isSetUp = false;
|
||||||
|
_workBuffer = null;
|
||||||
|
_logFilePosition = 0;
|
||||||
|
_bufferPosition = 0;
|
||||||
|
_mutex = new SdkMutex();
|
||||||
|
_fsClient = fsClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
DeallocateWorkBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void FinalizeObject()
|
||||||
|
{
|
||||||
|
using ScopedLock<SdkMutex> scopedLock = ScopedLock.Lock(ref _mutex);
|
||||||
|
|
||||||
|
FlushBuffer();
|
||||||
|
TearDown(writeEndTag: true);
|
||||||
|
_isSetUp = true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Flush()
|
||||||
|
{
|
||||||
|
using ScopedLock<SdkMutex> scopedLock = ScopedLock.Lock(ref _mutex);
|
||||||
|
|
||||||
|
FlushBuffer();
|
||||||
|
TearDown(writeEndTag: true);
|
||||||
|
_isSetUp = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AppendLog(ReadOnlySpan<byte> buffer, ulong processId)
|
||||||
|
{
|
||||||
|
using ScopedLock<SdkMutex> scopedLock = ScopedLock.Lock(ref _mutex);
|
||||||
|
|
||||||
|
if (buffer.Length > 0)
|
||||||
|
{
|
||||||
|
AppendProcessId(processId);
|
||||||
|
AppendBuffer(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool SetUp()
|
||||||
|
{
|
||||||
|
if (_isSetUp)
|
||||||
|
return _isLogFileOpen;
|
||||||
|
|
||||||
|
_isSetUp = true;
|
||||||
|
bool isSuccess = false;
|
||||||
|
|
||||||
|
Result res = _fsClient.MountSdCard(AccessLogMountName);
|
||||||
|
if (res.IsFailure()) return false;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
res = _fsClient.CreateFile(AccessLogFilePath, 0);
|
||||||
|
if (res.IsSuccess() || ResultFs.PathAlreadyExists.Includes(res))
|
||||||
|
{
|
||||||
|
res = _fsClient.OpenFile(out _fileHandle, AccessLogFilePath, OpenMode.All);
|
||||||
|
if (res.IsSuccess())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (WriteStartMarker())
|
||||||
|
{
|
||||||
|
_isLogFileOpen = true;
|
||||||
|
isSuccess = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (!isSuccess)
|
||||||
|
_fsClient.CloseFile(_fileHandle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (!isSuccess)
|
||||||
|
_fsClient.Unmount(AccessLogMountName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool WriteStartMarker()
|
||||||
|
{
|
||||||
|
Result res = _fsClient.GetFileSize(out _logFilePosition, _fileHandle);
|
||||||
|
if (res.IsFailure()) return false;
|
||||||
|
|
||||||
|
if (_logFilePosition <= 0)
|
||||||
|
{
|
||||||
|
res = _fsClient.WriteFile(_fileHandle, 0, BomUtf8, WriteOption.Flush);
|
||||||
|
if (res.IsFailure()) return false;
|
||||||
|
|
||||||
|
_logFilePosition = BomUtf8.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = _fsClient.WriteFile(_fileHandle, _logFilePosition, AccessLogStartMarker, WriteOption.Flush);
|
||||||
|
if (res.IsFailure()) return false;
|
||||||
|
|
||||||
|
_logFilePosition += AccessLogStartMarker.Length;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AppendBuffer(ReadOnlySpan<byte> buffer)
|
||||||
|
{
|
||||||
|
if (SetUp() && AllocateWorkBuffer())
|
||||||
|
{
|
||||||
|
if (buffer.Length > WorkBufferSize)
|
||||||
|
{
|
||||||
|
WriteWorkBuffer();
|
||||||
|
Write(buffer);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ((long)_bufferPosition + buffer.Length > WorkBufferSize)
|
||||||
|
WriteWorkBuffer();
|
||||||
|
|
||||||
|
buffer.CopyTo(_workBuffer.AsSpan(_bufferPosition));
|
||||||
|
_bufferPosition += buffer.Length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AppendProcessId(ulong processId)
|
||||||
|
{
|
||||||
|
Span<byte> buffer = stackalloc byte[0x16];
|
||||||
|
|
||||||
|
var sb = new U8StringBuilder(buffer);
|
||||||
|
sb.Append("(0x"u8).AppendFormat(processId, 'X', 16).Append(") "u8);
|
||||||
|
|
||||||
|
AppendBuffer(sb.Buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool AllocateWorkBuffer()
|
||||||
|
{
|
||||||
|
if (_workBuffer is not null)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
_workBuffer = ArrayPool<byte>.Shared.Rent(WorkBufferSize);
|
||||||
|
return _workBuffer is not null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DeallocateWorkBuffer()
|
||||||
|
{
|
||||||
|
if (_workBuffer is not null)
|
||||||
|
{
|
||||||
|
ArrayPool<byte>.Shared.Return(_workBuffer);
|
||||||
|
_workBuffer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TearDown(bool writeEndTag)
|
||||||
|
{
|
||||||
|
if (_isLogFileOpen)
|
||||||
|
{
|
||||||
|
if (writeEndTag)
|
||||||
|
{
|
||||||
|
_fsClient.WriteFile(_fileHandle, _logFilePosition, AccessLogEndMarker, WriteOption.Flush).IgnoreResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
_fsClient.CloseFile(_fileHandle);
|
||||||
|
_fsClient.Unmount(AccessLogMountName);
|
||||||
|
_isLogFileOpen = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Write(ReadOnlySpan<byte> buffer)
|
||||||
|
{
|
||||||
|
if (_isLogFileOpen)
|
||||||
|
{
|
||||||
|
if (_logFilePosition + buffer.Length < _logFilePosition)
|
||||||
|
{
|
||||||
|
TearDown(writeEndTag: true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Result res = _fsClient.WriteFile(_fileHandle, _logFilePosition, buffer, WriteOption.None);
|
||||||
|
|
||||||
|
if (res.IsSuccess())
|
||||||
|
{
|
||||||
|
_logFilePosition += buffer.Length;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TearDown(writeEndTag: false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteWorkBuffer()
|
||||||
|
{
|
||||||
|
if (_workBuffer is not null && _bufferPosition > 0)
|
||||||
|
{
|
||||||
|
Write(_workBuffer.AsSpan(0, _bufferPosition));
|
||||||
|
_bufferPosition = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FlushBuffer()
|
||||||
|
{
|
||||||
|
WriteWorkBuffer();
|
||||||
|
|
||||||
|
if (_isLogFileOpen)
|
||||||
|
{
|
||||||
|
Result res = _fsClient.FlushFile(_fileHandle);
|
||||||
|
if (res.IsFailure())
|
||||||
|
{
|
||||||
|
TearDown(writeEndTag: false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue