Implement DeleteSaveDataFileSystem

This commit is contained in:
Alex Barney 2019-10-21 00:29:54 -05:00
parent 92049bf9b7
commit e74bda1a68
9 changed files with 310 additions and 14 deletions

View file

@ -83,6 +83,15 @@ namespace LibHac.Fs
ExtensionInfo = 2
}
public enum SaveDataState : byte
{
Normal = 0,
Creating = 1,
State2 = 2,
MarkedForDeletion = 3,
State4 = 4,
}
public enum ImageDirectoryId
{
Nand = 0,

View file

@ -137,4 +137,19 @@ namespace LibHac.Fs
[FieldOffset(0x24)] public SaveDataSpaceId SpaceId;
[FieldOffset(0x25)] public bool Field25;
}
[StructLayout(LayoutKind.Explicit, Size = 0x60)]
public struct SaveDataInfo
{
[FieldOffset(0x00)] public ulong SaveDataId;
[FieldOffset(0x08)] public SaveDataSpaceId SpaceId;
[FieldOffset(0x09)] public SaveDataType Type;
[FieldOffset(0x10)] public UserId UserId;
[FieldOffset(0x20)] public ulong SaveDataIdFromKey;
[FieldOffset(0x28)] public TitleId TitleId;
[FieldOffset(0x30)] public long Size;
[FieldOffset(0x38)] public short Index;
[FieldOffset(0x3A)] public byte Rank;
[FieldOffset(0x3B)] public SaveDataState State;
}
}

View file

@ -38,7 +38,8 @@ namespace LibHac.FsService.Creators
switch (entryType)
{
case DirectoryEntryType.Directory:
if (!allowDirectorySaveData) return ResultFs.InvalidSaveDataEntryType.Log();
// Actual FS does this check
// if (!allowDirectorySaveData) return ResultFs.InvalidSaveDataEntryType.Log();
var subDirFs = new SubdirectoryFileSystem(sourceFileSystem, saveDataPath);

View file

@ -106,17 +106,148 @@ namespace LibHac.FsService
public Result DeleteSaveDataFileSystem(ulong saveDataId)
{
throw new NotImplementedException();
return DeleteSaveDataFileSystemImpl(SaveDataSpaceId.System, saveDataId);
}
private Result DeleteSaveDataFileSystemImpl(SaveDataSpaceId spaceId, ulong saveDataId)
{
SaveDataIndexerReader reader = default;
try
{
Result rc = FsServer.SaveDataIndexerManager.GetSaveDataIndexer(out reader, spaceId);
if (rc.IsFailure()) return rc;
if (saveDataId == FileSystemServer.SaveIndexerId)
{
// missing: This save can only be deleted by the FS process itself
}
else
{
if (spaceId != SaveDataSpaceId.ProperSystem && spaceId != SaveDataSpaceId.Safe)
{
rc = reader.Indexer.GetBySaveDataId(out SaveDataIndexerValue value, saveDataId);
if (rc.IsFailure()) return rc;
spaceId = value.SpaceId;
}
rc = reader.Indexer.GetKey(out SaveDataAttribute key, saveDataId);
if (rc.IsFailure()) return rc;
if (key.Type == SaveDataType.SystemSaveData || key.Type == SaveDataType.BcatSystemStorage)
{
// Check if permissions allow deleting system save data
}
else
{
// Check if permissions allow deleting save data
}
reader.Indexer.SetState(saveDataId, SaveDataState.Creating);
if (rc.IsFailure()) return rc;
reader.Indexer.Commit();
if (rc.IsFailure()) return rc;
}
// missing: Check extra data flags for this value. Bit 3
bool doSecureDelete = false;
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
rc = DeleteSaveDataFileSystemImpl2(spaceId, saveDataId, doSecureDelete);
if (rc.IsFailure()) return rc;
if (saveDataId != FileSystemServer.SaveIndexerId)
{
rc = reader.Indexer.Delete(saveDataId);
if (rc.IsFailure()) return rc;
rc = reader.Indexer.Commit();
if (rc.IsFailure()) return rc;
}
return Result.Success;
}
finally
{
reader.Dispose();
}
}
private Result DeleteSaveDataFileSystemImpl2(SaveDataSpaceId spaceId, ulong saveDataId, bool doSecureDelete)
{
Result rc = FsProxyCore.DeleteSaveDataMetaFiles(saveDataId, spaceId);
if (rc.IsFailure() && rc != ResultFs.PathNotFound)
return rc;
rc = FsProxyCore.DeleteSaveDataFileSystem(spaceId, saveDataId, doSecureDelete);
if (rc.IsFailure() && rc != ResultFs.PathNotFound)
return rc;
return Result.Success;
}
public Result DeleteSaveDataFileSystemBySaveDataSpaceId(SaveDataSpaceId spaceId, ulong saveDataId)
{
throw new NotImplementedException();
return DeleteSaveDataFileSystemBySaveDataSpaceIdImpl(spaceId, saveDataId);
}
public Result DeleteSaveDataFileSystemBySaveDataSpaceIdImpl(SaveDataSpaceId spaceId, ulong saveDataId)
{
if (saveDataId != FileSystemServer.SaveIndexerId)
{
SaveDataIndexerReader reader = default;
try
{
Result rc = FsServer.SaveDataIndexerManager.GetSaveDataIndexer(out reader, spaceId);
if (rc.IsFailure()) return rc;
rc = reader.Indexer.GetBySaveDataId(out SaveDataIndexerValue value, saveDataId);
if (rc.IsFailure()) return rc;
if (value.SpaceId != GetSpaceIdForIndexer(spaceId))
return ResultFs.TargetNotFound.Log();
}
finally
{
reader.Dispose();
}
}
return DeleteSaveDataFileSystemImpl(spaceId, saveDataId);
}
public Result GetSaveDataInfo(out SaveDataInfo info, SaveDataSpaceId spaceId, ref SaveDataAttribute attribute)
{
info = default;
SaveDataIndexerReader reader = default;
try
{
Result rc = FsServer.SaveDataIndexerManager.GetSaveDataIndexer(out reader, spaceId);
if (rc.IsFailure()) return rc;
rc = reader.Indexer.Get(out SaveDataIndexerValue value, ref attribute);
if (rc.IsFailure()) return rc;
SaveDataIndexer.GetSaveDataInfo(out info, ref attribute, ref value);
return Result.Success;
}
finally
{
reader.Dispose();
}
}
public Result DeleteSaveDataFileSystemBySaveDataAttribute(SaveDataSpaceId spaceId, ref SaveDataAttribute attribute)
{
throw new NotImplementedException();
Result rs = GetSaveDataInfo(out SaveDataInfo info, spaceId, ref attribute);
if (rs.IsFailure()) return rs;
return DeleteSaveDataFileSystemBySaveDataSpaceIdImpl(spaceId, info.SaveDataId);
}
public Result UpdateSaveDataMacForDebug(SaveDataSpaceId spaceId, ulong saveDataId)
@ -178,7 +309,7 @@ namespace LibHac.FsService
return ResultFs.PathAlreadyExists.LogConverted(rc);
}
rc = reader.Indexer.SetState(saveDataId, 1);
rc = reader.Indexer.SetState(saveDataId, SaveDataState.Creating);
if (rc.IsFailure()) return rc;
SaveDataSpaceId indexerSpaceId = GetSpaceIdForIndexer(createInfo.SpaceId);
@ -208,12 +339,12 @@ namespace LibHac.FsService
metaCreateInfo.Size);
if (rc.IsFailure()) return rc;
if(metaCreateInfo.Type == SaveMetaType.Thumbnail)
if (metaCreateInfo.Type == SaveMetaType.Thumbnail)
{
rc = FsProxyCore.OpenSaveDataMetaFile(out IFile metaFile, saveDataId, createInfo.SpaceId,
metaCreateInfo.Type);
using(metaFile)
using (metaFile)
{
if (rc.IsFailure()) return rc;
@ -282,7 +413,6 @@ namespace LibHac.FsService
public Result CreateSaveDataFileSystemBySystemSaveDataId(ref SaveDataAttribute attribute, ref SaveDataCreateInfo createInfo)
{
if (!IsSystemSaveDataId(attribute.SaveDataId))
return ResultFs.InvalidArgument.Log();
@ -340,7 +470,7 @@ namespace LibHac.FsService
if (indexerValue.SpaceId != indexerSpaceId)
return ResultFs.TargetNotFound.Log();
if (indexerValue.State == 4)
if (indexerValue.State == SaveDataState.State4)
return ResultFs.Result6906.Log();
saveDataId = indexerValue.SaveDataId;

View file

@ -334,6 +334,23 @@ namespace LibHac.FsService
return metaDirFs.OpenFile(out file, metaFilePath, OpenMode.ReadWrite);
}
public Result DeleteSaveDataMetaFiles(ulong saveDataId, SaveDataSpaceId spaceId)
{
Result rc = OpenSaveDataDirectoryImpl(out IFileSystem metaDirFs, spaceId, "/saveMeta", false);
using (metaDirFs)
{
if (rc.IsFailure()) return rc;
rc = metaDirFs.DeleteDirectoryRecursively($"/{saveDataId:x16}");
if (rc.IsFailure() && rc != ResultFs.PathNotFound)
return rc;
return Result.Success;
}
}
public Result CreateSaveDataMetaFile(ulong saveDataId, SaveDataSpaceId spaceId, SaveMetaType type, long size)
{
string metaDirPath = $"/saveMeta/{saveDataId:x16}";
@ -360,6 +377,38 @@ namespace LibHac.FsService
return fileSystem.EnsureDirectoryExists(GetSaveDataIdPath(saveDataId));
}
public Result DeleteSaveDataFileSystem(SaveDataSpaceId spaceId, ulong saveDataId, bool doSecureDelete)
{
Result rc = OpenSaveDataDirectory(out IFileSystem fileSystem, spaceId, string.Empty, false);
using (fileSystem)
{
if (rc.IsFailure()) return rc;
string saveDataPath = GetSaveDataIdPath(saveDataId);
rc = fileSystem.GetEntryType(out DirectoryEntryType entryType, saveDataPath);
if (rc.IsFailure()) return rc;
if (entryType == DirectoryEntryType.Directory)
{
rc = fileSystem.DeleteDirectoryRecursively(saveDataPath);
}
else
{
if (doSecureDelete)
{
// Overwrite file with garbage before deleting
throw new NotImplementedException();
}
rc = fileSystem.DeleteFile(saveDataPath);
}
return rc;
}
}
public Result SetGlobalAccessLogMode(GlobalAccessLogMode mode)
{
LogMode = mode;

View file

@ -9,9 +9,11 @@ namespace LibHac.FsService
Result Get(out SaveDataIndexerValue value, ref SaveDataAttribute key);
Result AddSystemSaveData(ref SaveDataAttribute key);
bool IsFull();
Result Delete(ulong saveDataId);
Result SetSpaceId(ulong saveDataId, SaveDataSpaceId spaceId);
Result SetSize(ulong saveDataId, long size);
Result SetState(ulong saveDataId, byte state);
Result SetState(ulong saveDataId, SaveDataState state);
Result GetKey(out SaveDataAttribute key, ulong saveDataId);
Result GetBySaveDataId(out SaveDataIndexerValue value, ulong saveDataId);
}
}

View file

@ -32,6 +32,23 @@ namespace LibHac.FsService
SpaceId = spaceId;
}
public static void GetSaveDataInfo(out SaveDataInfo info, ref SaveDataAttribute key, ref SaveDataIndexerValue value)
{
info = new SaveDataInfo
{
SaveDataId = value.SaveDataId,
SpaceId = value.SpaceId,
Type = key.Type,
UserId = key.UserId,
SaveDataIdFromKey = key.SaveDataId,
TitleId = key.TitleId,
Size = value.Size,
Index = key.Index,
Rank = key.Rank,
State = value.State
};
}
public Result Commit()
{
lock (Locker)
@ -192,10 +209,35 @@ namespace LibHac.FsService
return false;
}
public Result Delete(ulong saveDataId)
{
lock (Locker)
{
Result rc = Initialize();
if (rc.IsFailure()) return rc;
rc = EnsureKvDatabaseLoaded(false);
if (rc.IsFailure()) return rc;
if (!TryGetBySaveDataIdInternal(out SaveDataAttribute key, out _, saveDataId))
{
return ResultFs.TargetNotFound.Log();
}
return KvDatabase.Delete(ref key);
}
}
public Result SetSpaceId(ulong saveDataId, SaveDataSpaceId spaceId)
{
lock (Locker)
{
Result rc = Initialize();
if (rc.IsFailure()) return rc;
rc = EnsureKvDatabaseLoaded(false);
if (rc.IsFailure()) return rc;
if (!TryGetBySaveDataIdInternal(out SaveDataAttribute key, out SaveDataIndexerValue value, saveDataId))
{
return ResultFs.TargetNotFound.Log();
@ -211,6 +253,12 @@ namespace LibHac.FsService
{
lock (Locker)
{
Result rc = Initialize();
if (rc.IsFailure()) return rc;
rc = EnsureKvDatabaseLoaded(false);
if (rc.IsFailure()) return rc;
if (!TryGetBySaveDataIdInternal(out SaveDataAttribute key, out SaveDataIndexerValue value, saveDataId))
{
return ResultFs.TargetNotFound.Log();
@ -222,10 +270,16 @@ namespace LibHac.FsService
}
}
public Result SetState(ulong saveDataId, byte state)
public Result SetState(ulong saveDataId, SaveDataState state)
{
lock (Locker)
{
Result rc = Initialize();
if (rc.IsFailure()) return rc;
rc = EnsureKvDatabaseLoaded(false);
if (rc.IsFailure()) return rc;
if (!TryGetBySaveDataIdInternal(out SaveDataAttribute key, out SaveDataIndexerValue value, saveDataId))
{
return ResultFs.TargetNotFound.Log();
@ -237,10 +291,39 @@ namespace LibHac.FsService
}
}
public Result GetBySaveDataId(out SaveDataIndexerValue value, ulong saveDataId)
public Result GetKey(out SaveDataAttribute key, ulong saveDataId)
{
key = default;
lock (Locker)
{
Result rc = Initialize();
if (rc.IsFailure()) return rc;
rc = EnsureKvDatabaseLoaded(false);
if (rc.IsFailure()) return rc;
if (TryGetBySaveDataIdInternal(out key, out _, saveDataId))
{
return Result.Success;
}
return ResultFs.TargetNotFound.Log();
}
}
public Result GetBySaveDataId(out SaveDataIndexerValue value, ulong saveDataId)
{
value = default;
lock (Locker)
{
Result rc = Initialize();
if (rc.IsFailure()) return rc;
rc = EnsureKvDatabaseLoaded(false);
if (rc.IsFailure()) return rc;
if (TryGetBySaveDataIdInternal(out _, out value, saveDataId))
{
return Result.Success;

View file

@ -10,6 +10,6 @@ namespace LibHac.FsService
[FieldOffset(0x08)] public long Size;
[FieldOffset(0x10)] public ulong Field10;
[FieldOffset(0x18)] public SaveDataSpaceId SpaceId;
[FieldOffset(0x19)] public byte State;
[FieldOffset(0x19)] public SaveDataState State;
}
}

View file

@ -10,7 +10,7 @@ namespace LibHac.Kvdb
{
public class KeyValueDatabase<TKey> where TKey : unmanaged, IComparable<TKey>, IEquatable<TKey>
{
public Dictionary<TKey, byte[]> KvDict { get; } = new Dictionary<TKey, byte[]>();
private Dictionary<TKey, byte[]> KvDict { get; } = new Dictionary<TKey, byte[]>();
private FileSystemClient FsClient { get; }
private string FileName { get; }
@ -51,6 +51,13 @@ namespace LibHac.Kvdb
return Result.Success;
}
public Result Delete(ref TKey key)
{
bool deleted = KvDict.Remove(key);
return deleted ? Result.Success : ResultKvdb.KeyNotFound.Log();
}
public Dictionary<TKey, byte[]>.Enumerator GetEnumerator()
{
return KvDict.GetEnumerator();