diff --git a/src/LibHac/FsSrv/AccessLogService.cs b/src/LibHac/FsSrv/AccessLogService.cs
index e81e3018..5bd6dbe3 100644
--- a/src/LibHac/FsSrv/AccessLogService.cs
+++ b/src/LibHac/FsSrv/AccessLogService.cs
@@ -1,15 +1,34 @@
using System;
+using LibHac.Common;
using LibHac.Fs;
using LibHac.FsSrv.Impl;
using LibHac.Sf;
+using static LibHac.FsSrv.Anonymous;
namespace LibHac.FsSrv;
+file static class Anonymous
+{
+ public static Result GetProgramInfo(FileSystemServer fsServer, out ProgramInfo programInfo, ulong processId)
+ {
+ var programRegistry = new ProgramRegistryImpl(fsServer);
+ return programRegistry.GetProgramInfo(out programInfo, processId).Ret();
+ }
+}
+
+///
+/// Handles access log calls for .
+///
+/// This struct handles checking a process' permissions before forwarding
+/// requests to the object.
+/// Based on nnSdk 18.3.0 (FS 18.0.0)
internal readonly struct AccessLogService
{
private readonly AccessLogServiceImpl _serviceImpl;
private readonly ulong _processId;
+ private FileSystemServer FsServer => _serviceImpl.FsServer;
+
public AccessLogService(AccessLogServiceImpl serviceImpl, ulong processId)
{
_serviceImpl = serviceImpl;
@@ -18,7 +37,7 @@ internal readonly struct AccessLogService
public Result SetAccessLogMode(GlobalAccessLogMode mode)
{
- Result res = GetProgramInfo(out ProgramInfo programInfo);
+ Result res = GetProgramInfo(FsServer, out ProgramInfo programInfo, _processId);
if (res.IsFailure()) return res.Miss();
if (!programInfo.AccessControl.CanCall(OperationType.SetGlobalAccessLogMode))
@@ -36,15 +55,46 @@ internal readonly struct AccessLogService
public Result OutputAccessLogToSdCard(InBuffer textBuffer)
{
- Result res = GetProgramInfo(out ProgramInfo programInfo);
+ Result res = GetProgramInfo(FsServer, out ProgramInfo programInfo, _processId);
if (res.IsFailure()) return res.Miss();
- return _serviceImpl.OutputAccessLogToSdCard(textBuffer.Buffer, programInfo.ProgramIdValue, _processId);
+ return _serviceImpl.OutputAccessLogToSdCard(textBuffer.Buffer, programInfo.ProgramIdValue, _processId).Ret();
}
+ private static ReadOnlySpan OutputAccessLog => "FS_ACCESS: { multi_program_tag: true }\n"u8;
+ private static ReadOnlySpan OutputLogTagUnknown => "FS_ACCESS: { application_info_tag: { launch_type: Unknown } }\n"u8;
+ private static ReadOnlySpan OutputHead => "FS_ACCESS: { application_info_tag: { launch_type: "u8;
+ private static ReadOnlySpan OutputApplicationId => "Application, application_id: 0x"u8;
+ private static ReadOnlySpan OutputPatchId => "Patch, application_id: 0x"u8;
+ private static ReadOnlySpan OutputVersion => ", release_version: 0x"u8;
+ private static ReadOnlySpan OutputTail => " } }\n"u8;
+
public Result OutputApplicationInfoAccessLog(in ApplicationInfo applicationInfo)
{
- throw new NotImplementedException();
+ if (applicationInfo.IsMultiProgram)
+ {
+ _serviceImpl.OutputAccessLogToSdCard(OutputAccessLog, applicationInfo.ApplicationId.Value, _processId).IgnoreResult();
+ }
+
+ if (applicationInfo.LaunchType == 0)
+ {
+ _serviceImpl.OutputAccessLogToSdCard(OutputLogTagUnknown, applicationInfo.ApplicationId.Value, _processId).IgnoreResult();
+ }
+ else
+ {
+ Span buffer = stackalloc byte[0x80];
+ ReadOnlySpan outputId = applicationInfo.LaunchType == 1 ? OutputApplicationId : OutputPatchId;
+
+ var sb = new U8StringBuilder(buffer, autoExpand: true);
+ sb.Append(OutputHead)
+ .Append(outputId).AppendFormat(applicationInfo.ApplicationId.Value, 'X', 16)
+ .Append(OutputVersion).AppendFormat(applicationInfo.Version >> 16, 'X', 4)
+ .Append(OutputTail);
+
+ _serviceImpl.OutputAccessLogToSdCard(sb.Buffer, applicationInfo.ApplicationId.Value, _processId);
+ }
+
+ return Result.Success;
}
public Result OutputMultiProgramTagAccessLog()
@@ -55,12 +105,8 @@ internal readonly struct AccessLogService
public Result FlushAccessLogOnSdCard()
{
- throw new NotImplementedException();
- }
-
- private Result GetProgramInfo(out ProgramInfo programInfo)
- {
- return _serviceImpl.GetProgramInfo(out programInfo, _processId);
+ _serviceImpl.FlushAccessLogSdCardWriter();
+ return Result.Success;
}
/// "FS_ACCESS: { multi_program_tag: true }\n"
diff --git a/src/LibHac/FsSrv/AccessLogServiceImpl.cs b/src/LibHac/FsSrv/AccessLogServiceImpl.cs
index a919208c..90cba0dd 100644
--- a/src/LibHac/FsSrv/AccessLogServiceImpl.cs
+++ b/src/LibHac/FsSrv/AccessLogServiceImpl.cs
@@ -1,34 +1,56 @@
using System;
+using LibHac.Diag;
using LibHac.Fs;
using LibHac.FsSrv.Impl;
+using LibHac.Os;
+using Utility = LibHac.FsSrv.Impl.Utility;
namespace LibHac.FsSrv;
+///
+/// Writes to the FS access log file on the SD card, filtering messages based on the current global log mode
+/// and the sending program's ID.
+///
+/// Based on nnSdk 18.3.0 (FS 18.0.0)
public class AccessLogServiceImpl : IDisposable
{
private Configuration _config;
private GlobalAccessLogMode _accessLogMode;
+ private AccessLogSdCardWriter _sdCardWriter;
+ private SdkMutexType _mutex;
- public AccessLogServiceImpl(in Configuration configuration)
- {
- _config = configuration;
- }
-
- public void Dispose()
- {
-
- }
+ public FileSystemServer FsServer => _config.FsServer;
public struct Configuration
{
- public ulong MinimumProgramIdForSdCardLog;
+ public ulong ProgramIdWithoutPlatformIdMinForAccessLog;
// LibHac additions
public FileSystemServer FsServer;
}
+ public AccessLogServiceImpl(in Configuration configuration)
+ {
+ _config = configuration;
+ _accessLogMode = GlobalAccessLogMode.None;
+ _sdCardWriter = new AccessLogSdCardWriter(configuration.FsServer.Hos.Fs);
+ _mutex = new SdkMutexType();
+ }
+
+ public void Dispose()
+ {
+ _sdCardWriter.Dispose();
+ }
+
public void SetAccessLogMode(GlobalAccessLogMode mode)
{
+ using ScopedLock scopedLock = ScopedLock.Lock(ref _mutex);
+
+ if (_accessLogMode.HasFlag(GlobalAccessLogMode.SdCard) && !mode.HasFlag(GlobalAccessLogMode.SdCard))
+ {
+ _sdCardWriter.Flush();
+ }
+
_accessLogMode = mode;
}
@@ -44,22 +66,31 @@ public class AccessLogServiceImpl : IDisposable
public Result OutputAccessLogToSdCard(ReadOnlySpan text, ulong programId, ulong processId)
{
- throw new NotImplementedException();
+ using ScopedLock scopedLock = ScopedLock.Lock(ref _mutex);
+
+ if (!_accessLogMode.HasFlag(GlobalAccessLogMode.SdCard))
+ return Result.Success;
+
+ Assert.SdkRequiresNotEqual(_config.ProgramIdWithoutPlatformIdMinForAccessLog, 0ul);
+
+ if (Utility.ClearPlatformIdInProgramId(processId) >= _config.ProgramIdWithoutPlatformIdMinForAccessLog)
+ {
+ _sdCardWriter.AppendLog(text, programId);
+ }
+
+ return Result.Success;
}
- public Result FlushAccessLogSdCardWriter()
+ public void FlushAccessLogSdCardWriter()
{
- throw new NotImplementedException();
+ if (_accessLogMode.HasFlag(GlobalAccessLogMode.SdCard))
+ {
+ _sdCardWriter.Flush();
+ }
}
- public Result FinalizeAccessLogSdCardWriter()
+ public void FinalizeAccessLogSdCardWriter()
{
- throw new NotImplementedException();
- }
-
- internal Result GetProgramInfo(out ProgramInfo programInfo, ulong processId)
- {
- var registry = new ProgramRegistryImpl(_config.FsServer);
- return registry.GetProgramInfo(out programInfo, processId);
+ _sdCardWriter.FinalizeObject();
}
}
\ No newline at end of file
diff --git a/src/LibHac/FsSrv/FileSystemServerInitializer.cs b/src/LibHac/FsSrv/FileSystemServerInitializer.cs
index 7c401007..4f5f77ef 100644
--- a/src/LibHac/FsSrv/FileSystemServerInitializer.cs
+++ b/src/LibHac/FsSrv/FileSystemServerInitializer.cs
@@ -170,7 +170,7 @@ public static class FileSystemServerInitializer
var statusReportService = new StatusReportServiceImpl(in statusReportServiceConfig);
var accessLogServiceConfig = new AccessLogServiceImpl.Configuration();
- accessLogServiceConfig.MinimumProgramIdForSdCardLog = 0x0100000000003000;
+ accessLogServiceConfig.ProgramIdWithoutPlatformIdMinForAccessLog = 0x3000;
accessLogServiceConfig.FsServer = server;
var accessLogService = new AccessLogServiceImpl(in accessLogServiceConfig);