Add a basic SaveDataFileSystemCreator

This commit is contained in:
Alex Barney 2019-10-01 16:31:21 -05:00
parent 5011a57d3e
commit d7f3e94577
13 changed files with 188 additions and 64 deletions

View file

@ -189,7 +189,7 @@ namespace LibHac.Fs
rc = operation(); rc = operation();
TimeSpan endTime = Time.GetCurrent(); TimeSpan endTime = Time.GetCurrent();
OutputAccessLog(rc, startTime, endTime, textGenerator(), caller); OutputAccessLog(rc, startTime, endTime, handle, textGenerator(), caller);
} }
else else
{ {

View file

@ -2,8 +2,9 @@
{ {
public interface IAttributeFileSystem : IFileSystem public interface IAttributeFileSystem : IFileSystem
{ {
NxFileAttributes GetFileAttributes(string path); Result CreateDirectory(string path, NxFileAttributes archiveAttribute);
void SetFileAttributes(string path, NxFileAttributes attributes); Result GetFileAttributes(string path, out NxFileAttributes attributes);
Result SetFileAttributes(string path, NxFileAttributes attributes);
long GetFileSize(string path); long GetFileSize(string path);
} }
} }

View file

@ -27,6 +27,7 @@
public static Result InvalidIndirectStorageSource => new Result(ModuleFs, 4023); public static Result InvalidIndirectStorageSource => new Result(ModuleFs, 4023);
public static Result Result4302 => new Result(ModuleFs, 4302); public static Result Result4302 => new Result(ModuleFs, 4302);
public static Result InvalidSaveDataEntryType => new Result(ModuleFs, 4303);
public static Result InvalidSaveDataHeader => new Result(ModuleFs, 4315); public static Result InvalidSaveDataHeader => new Result(ModuleFs, 4315);
public static Result Result4362 => new Result(ModuleFs, 4362); public static Result Result4362 => new Result(ModuleFs, 4362);
public static Result Result4363 => new Result(ModuleFs, 4363); public static Result Result4363 => new Result(ModuleFs, 4363);

View file

@ -5,6 +5,11 @@ namespace LibHac.Fs
{ {
public static class SaveData public static class SaveData
{ {
public static Result MountSystemSaveData(this FileSystemClient fs, U8Span mountName, SaveDataSpaceId spaceId, ulong saveDataId)
{
return MountSystemSaveData(fs, mountName, spaceId, saveDataId, UserId.EmptyId);
}
public static Result MountSystemSaveData(this FileSystemClient fs, U8Span mountName, public static Result MountSystemSaveData(this FileSystemClient fs, U8Span mountName,
SaveDataSpaceId spaceId, ulong saveDataId, UserId userId) SaveDataSpaceId spaceId, ulong saveDataId, UserId userId)
{ {
@ -67,19 +72,19 @@ namespace LibHac.Fs
public static Result CreateSystemSaveData(this FileSystemClient fs, ulong saveDataId, ulong ownerId, long size, public static Result CreateSystemSaveData(this FileSystemClient fs, ulong saveDataId, ulong ownerId, long size,
long journalSize, uint flags) long journalSize, uint flags)
{ {
return CreateSystemSaveData(fs, SaveDataSpaceId.System, saveDataId, new UserId(0, 0), ownerId, size, journalSize, flags); return CreateSystemSaveData(fs, SaveDataSpaceId.System, saveDataId, UserId.EmptyId, ownerId, size, journalSize, flags);
} }
public static Result CreateSystemSaveData(this FileSystemClient fs, ulong saveDataId, long size, public static Result CreateSystemSaveData(this FileSystemClient fs, ulong saveDataId, long size,
long journalSize, uint flags) long journalSize, uint flags)
{ {
return CreateSystemSaveData(fs, SaveDataSpaceId.System, saveDataId, new UserId(0, 0), 0, size, journalSize, flags); return CreateSystemSaveData(fs, SaveDataSpaceId.System, saveDataId, UserId.EmptyId, 0, size, journalSize, flags);
} }
public static Result CreateSystemSaveData(this FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId, public static Result CreateSystemSaveData(this FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId,
ulong ownerId, long size, long journalSize, uint flags) ulong ownerId, long size, long journalSize, uint flags)
{ {
return CreateSystemSaveData(fs, spaceId, saveDataId, new UserId(0, 0), ownerId, size, journalSize, flags); return CreateSystemSaveData(fs, spaceId, saveDataId, UserId.EmptyId, ownerId, size, journalSize, flags);
} }
public static Result DeleteSaveData(this FileSystemClient fs, ulong saveDataId) public static Result DeleteSaveData(this FileSystemClient fs, ulong saveDataId)

View file

@ -7,6 +7,8 @@ namespace LibHac.Fs
[StructLayout(LayoutKind.Sequential, Size = 0x10)] [StructLayout(LayoutKind.Sequential, Size = 0x10)]
public struct UserId : IEquatable<UserId>, IComparable<UserId>, IComparable public struct UserId : IEquatable<UserId>, IComparable<UserId>, IComparable
{ {
public static readonly UserId EmptyId = new UserId(0, 0);
public readonly Id128 Id; public readonly Id128 Id;
public UserId(ulong high, ulong low) public UserId(ulong high, ulong low)

View file

@ -26,6 +26,7 @@ namespace LibHac.FsService.Creators
var creators = new FileSystemCreators(); var creators = new FileSystemCreators();
creators.SubDirectoryFileSystemCreator = new SubDirectoryFileSystemCreator(); creators.SubDirectoryFileSystemCreator = new SubDirectoryFileSystemCreator();
creators.SaveDataFileSystemCreator = new SaveDataFileSystemCreator(keyset);
creators.EncryptedFileSystemCreator = new EncryptedFileSystemCreator(keyset); creators.EncryptedFileSystemCreator = new EncryptedFileSystemCreator(keyset);
creators.BuiltInStorageFileSystemCreator = new EmulatedBisFileSystemCreator(rootFileSystem); creators.BuiltInStorageFileSystemCreator = new EmulatedBisFileSystemCreator(rootFileSystem);
creators.SdFileSystemCreator = new EmulatedSdFileSystemCreator(rootFileSystem); creators.SdFileSystemCreator = new EmulatedSdFileSystemCreator(rootFileSystem);

View file

@ -0,0 +1,70 @@
using System;
using LibHac.Common;
using LibHac.Fs;
using LibHac.FsSystem;
using LibHac.FsSystem.Save;
namespace LibHac.FsService.Creators
{
public class SaveDataFileSystemCreator : ISaveDataFileSystemCreator
{
private Keyset Keyset { get; }
public SaveDataFileSystemCreator(Keyset keyset)
{
Keyset = keyset;
}
public Result CreateFile(out IFile file, IFileSystem sourceFileSystem, ulong saveDataId, OpenMode openMode)
{
throw new NotImplementedException();
}
public Result Create(out IFileSystem fileSystem, out ISaveDataExtraDataAccessor extraDataAccessor,
IFileSystem sourceFileSystem, ulong saveDataId, bool allowDirectorySaveData, bool useDeviceUniqueMac,
SaveDataType type, ITimeStampGenerator timeStampGenerator)
{
fileSystem = default;
extraDataAccessor = default;
string saveDataPath = $"/{saveDataId:x16}";
Result rc = sourceFileSystem.GetEntryType(out DirectoryEntryType entryType, saveDataPath);
if (rc.IsFailure())
{
return rc == ResultFs.PathNotFound ? ResultFs.TargetNotFound : rc;
}
switch (entryType)
{
case DirectoryEntryType.Directory:
if (!allowDirectorySaveData) return ResultFs.InvalidSaveDataEntryType.Log();
var subDirFs = new SubdirectoryFileSystem(sourceFileSystem, saveDataPath);
fileSystem = new DirectorySaveDataFileSystem(subDirFs);
// Todo: Dummy ISaveDataExtraDataAccessor
return Result.Success;
case DirectoryEntryType.File:
rc = sourceFileSystem.OpenFile(out IFile saveDataFile, saveDataPath, OpenMode.ReadWrite);
if (rc.IsFailure()) return rc;
var saveDataStorage = new FileStorage(saveDataFile);
fileSystem = new SaveDataFileSystem(Keyset, saveDataStorage, IntegrityCheckLevel.ErrorOnInvalid, true);
// Todo: ISaveDataExtraDataAccessor
return Result.Success;
default:
throw new ArgumentOutOfRangeException();
}
}
public void SetSdCardEncryptionSeed(ReadOnlySpan<byte> seed)
{
throw new NotImplementedException();
}
}
}

View file

@ -163,7 +163,7 @@ namespace LibHac.FsService
if (saveFsResult.IsSuccess()) return Result.Success; if (saveFsResult.IsSuccess()) return Result.Success;
if (saveFsResult == ResultFs.PathNotFound || saveFsResult == ResultFs.TargetNotFound) return saveFsResult; if (saveFsResult != ResultFs.PathNotFound && saveFsResult != ResultFs.TargetNotFound) return saveFsResult;
if (saveDataId != SaveIndexerId) if (saveDataId != SaveIndexerId)
{ {

View file

@ -54,14 +54,20 @@ namespace LibHac.FsSystem
#if CROSS_PLATFORM #if CROSS_PLATFORM
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{ {
return HasConcatenationFileAttribute(BaseFileSystem.GetFileAttributes(path)); Result rc = BaseFileSystem.GetFileAttributes(path, out NxFileAttributes attributes);
if (rc.IsFailure()) return false;
return HasConcatenationFileAttribute(attributes);
} }
else else
{ {
return IsConcatenationFileHeuristic(path); return IsConcatenationFileHeuristic(path);
} }
#else #else
return HasConcatenationFileAttribute(BaseFileSystem.GetFileAttributes(path)); Result rc = BaseFileSystem.GetFileAttributes(path, out NxFileAttributes attributes);
if (rc.IsFailure()) return false;
return HasConcatenationFileAttribute(attributes);
#endif #endif
} }
@ -93,11 +99,9 @@ namespace LibHac.FsSystem
return (attributes & NxFileAttributes.Directory) != 0 && (attributes & NxFileAttributes.Archive) != 0; return (attributes & NxFileAttributes.Directory) != 0 && (attributes & NxFileAttributes.Archive) != 0;
} }
private void SetConcatenationFileAttribute(string path) private Result SetConcatenationFileAttribute(string path)
{ {
NxFileAttributes attributes = BaseFileSystem.GetFileAttributes(path); return BaseFileSystem.SetFileAttributes(path, NxFileAttributes.Archive);
attributes |= NxFileAttributes.Archive;
BaseFileSystem.SetFileAttributes(path, attributes);
} }
public Result CreateDirectory(string path) public Result CreateDirectory(string path)
@ -134,11 +138,9 @@ namespace LibHac.FsSystem
return ResultFs.PathNotFound.Log(); return ResultFs.PathNotFound.Log();
} }
Result rc = BaseFileSystem.CreateDirectory(path); Result rc = BaseFileSystem.CreateDirectory(path, NxFileAttributes.Archive);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
SetConcatenationFileAttribute(path);
long remaining = size; long remaining = size;
for (int i = 0; remaining > 0; i++) for (int i = 0; remaining > 0; i++)
@ -320,9 +322,7 @@ namespace LibHac.FsSystem
{ {
if (queryId != QueryId.MakeConcatFile) return ResultFs.UnsupportedOperationInConcatFsQueryEntry.Log(); if (queryId != QueryId.MakeConcatFile) return ResultFs.UnsupportedOperationInConcatFsQueryEntry.Log();
SetConcatenationFileAttribute(path); return SetConcatenationFileAttribute(path);
return Result.Success;
} }
private int GetSubFileCount(string dirPath) private int GetSubFileCount(string dirPath)

View file

@ -213,7 +213,7 @@ namespace LibHac.FsSystem
rc = BaseFs.CreateDirectory(dest); rc = BaseFs.CreateDirectory(dest);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
return this.CopyDirectory(this, src, dest); return BaseFs.CopyDirectory(BaseFs, src, dest);
} }
internal void NotifyCloseWritableFile() internal void NotifyCloseWritableFile()

View file

@ -182,10 +182,18 @@ namespace LibHac.FsSystem
return (NxFileAttributes)(((int)attributes >> 4) & 3); return (NxFileAttributes)(((int)attributes >> 4) & 3);
} }
public static FileAttributes ToFatAttributes(this NxFileAttributes attributes)
{
return (FileAttributes)(((int)attributes & 3) << 4);
}
public static FileAttributes ApplyNxAttributes(this FileAttributes attributes, NxFileAttributes nxAttributes) public static FileAttributes ApplyNxAttributes(this FileAttributes attributes, NxFileAttributes nxAttributes)
{ {
var nxAttributeBits = (FileAttributes)(((int)nxAttributes & 3) << 4); // The only 2 bits from FileAttributes that are used in NxFileAttributes
return attributes | nxAttributeBits; const int mask = 3 << 4;
FileAttributes oldAttributes = attributes & (FileAttributes)mask;
return oldAttributes | nxAttributes.ToFatAttributes();
} }
public static void SetConcatenationFileAttribute(this IFileSystem fs, string path) public static void SetConcatenationFileAttribute(this IFileSystem fs, string path)

View file

@ -34,21 +34,46 @@ namespace LibHac.FsSystem
return PathTools.Combine(BasePath, path); return PathTools.Combine(BasePath, path);
} }
public NxFileAttributes GetFileAttributes(string path) public Result GetFileAttributes(string path, out NxFileAttributes attributes)
{ {
path = PathTools.Normalize(path); string localPath = ResolveLocalPath(PathTools.Normalize(path));
return File.GetAttributes(ResolveLocalPath(path)).ToNxAttributes();
FileInfo info = GetFileInfo(localPath);
if (info.Attributes == (FileAttributes)(-1))
{
attributes = default;
return ResultFs.PathNotFound.Log();
}
attributes = info.Attributes.ToNxAttributes();
return Result.Success;
} }
public void SetFileAttributes(string path, NxFileAttributes attributes) public Result SetFileAttributes(string path, NxFileAttributes attributes)
{ {
path = PathTools.Normalize(path); string localPath = ResolveLocalPath(PathTools.Normalize(path));
string localPath = ResolveLocalPath(path);
FileAttributes attributesOld = File.GetAttributes(localPath); FileInfo info = GetFileInfo(localPath);
if (info.Attributes == (FileAttributes)(-1))
{
return ResultFs.PathNotFound.Log();
}
FileAttributes attributesOld = info.Attributes;
FileAttributes attributesNew = attributesOld.ApplyNxAttributes(attributes); FileAttributes attributesNew = attributesOld.ApplyNxAttributes(attributes);
File.SetAttributes(localPath, attributesNew); try
{
info.Attributes = attributesNew;
}
catch (IOException)
{
return ResultFs.PathNotFound.Log();
}
return Result.Success;
} }
public long GetFileSize(string path) public long GetFileSize(string path)
@ -60,6 +85,11 @@ namespace LibHac.FsSystem
} }
public Result CreateDirectory(string path) public Result CreateDirectory(string path)
{
return CreateDirectory(path, NxFileAttributes.None);
}
public Result CreateDirectory(string path, NxFileAttributes archiveAttribute)
{ {
string localPath = ResolveLocalPath(PathTools.Normalize(path)); string localPath = ResolveLocalPath(PathTools.Normalize(path));
@ -75,7 +105,7 @@ namespace LibHac.FsSystem
return ResultFs.PathNotFound.Log(); return ResultFs.PathNotFound.Log();
} }
return CreateDirInternal(dir); return CreateDirInternal(dir, archiveAttribute);
} }
public Result CreateFile(string path, long size, CreateFileOptions options) public Result CreateFile(string path, long size, CreateFileOptions options)
@ -394,11 +424,17 @@ namespace LibHac.FsSystem
return Result.Success; return Result.Success;
} }
private static Result CreateDirInternal(DirectoryInfo dir) private static Result CreateDirInternal(DirectoryInfo dir, NxFileAttributes attributes)
{ {
try try
{ {
dir.Create(); dir.Create();
dir.Refresh();
if (attributes.HasFlag(NxFileAttributes.Archive))
{
dir.Attributes |= FileAttributes.Archive;
}
} }
catch (DirectoryNotFoundException) catch (DirectoryNotFoundException)
{ {

View file

@ -21,81 +21,81 @@ namespace LibHac.FsSystem
public Result CreateDirectory(string path) public Result CreateDirectory(string path)
{ {
path = PathTools.Normalize(path); string fullPath = ResolveFullPath(PathTools.Normalize(path));
return ParentFileSystem.CreateDirectory(ResolveFullPath(path)); return ParentFileSystem.CreateDirectory(fullPath);
} }
public Result CreateFile(string path, long size, CreateFileOptions options) public Result CreateFile(string path, long size, CreateFileOptions options)
{ {
path = PathTools.Normalize(path); string fullPath = ResolveFullPath(PathTools.Normalize(path));
return ParentFileSystem.CreateFile(ResolveFullPath(path), size, options); return ParentFileSystem.CreateFile(fullPath, size, options);
} }
public Result DeleteDirectory(string path) public Result DeleteDirectory(string path)
{ {
path = PathTools.Normalize(path); string fullPath = ResolveFullPath(PathTools.Normalize(path));
return ParentFileSystem.DeleteDirectory(ResolveFullPath(path)); return ParentFileSystem.DeleteDirectory(fullPath);
} }
public Result DeleteDirectoryRecursively(string path) public Result DeleteDirectoryRecursively(string path)
{ {
path = PathTools.Normalize(path); string fullPath = ResolveFullPath(PathTools.Normalize(path));
return ParentFileSystem.DeleteDirectoryRecursively(ResolveFullPath(path)); return ParentFileSystem.DeleteDirectoryRecursively(fullPath);
} }
public Result CleanDirectoryRecursively(string path) public Result CleanDirectoryRecursively(string path)
{ {
path = PathTools.Normalize(path); string fullPath = ResolveFullPath(PathTools.Normalize(path));
return ParentFileSystem.CleanDirectoryRecursively(ResolveFullPath(path)); return ParentFileSystem.CleanDirectoryRecursively(fullPath);
} }
public Result DeleteFile(string path) public Result DeleteFile(string path)
{ {
path = PathTools.Normalize(path); string fullPath = ResolveFullPath(PathTools.Normalize(path));
return ParentFileSystem.DeleteFile(ResolveFullPath(path)); return ParentFileSystem.DeleteFile(fullPath);
} }
public Result OpenDirectory(out IDirectory directory, string path, OpenDirectoryMode mode) public Result OpenDirectory(out IDirectory directory, string path, OpenDirectoryMode mode)
{ {
path = PathTools.Normalize(path); string fullPath = ResolveFullPath(PathTools.Normalize(path));
return ParentFileSystem.OpenDirectory(out directory, ResolveFullPath(path), mode); return ParentFileSystem.OpenDirectory(out directory, fullPath, mode);
} }
public Result OpenFile(out IFile file, string path, OpenMode mode) public Result OpenFile(out IFile file, string path, OpenMode mode)
{ {
path = PathTools.Normalize(path); string fullPath = ResolveFullPath(PathTools.Normalize(path));
return ParentFileSystem.OpenFile(out file, ResolveFullPath(path), mode); return ParentFileSystem.OpenFile(out file, fullPath, mode);
} }
public Result RenameDirectory(string oldPath, string newPath) public Result RenameDirectory(string oldPath, string newPath)
{ {
oldPath = PathTools.Normalize(oldPath); string fullOldPath = ResolveFullPath(PathTools.Normalize(oldPath));
newPath = PathTools.Normalize(newPath); string fullNewPath = ResolveFullPath(PathTools.Normalize(newPath));
return ParentFileSystem.RenameDirectory(oldPath, newPath); return ParentFileSystem.RenameDirectory(fullOldPath, fullNewPath);
} }
public Result RenameFile(string oldPath, string newPath) public Result RenameFile(string oldPath, string newPath)
{ {
oldPath = PathTools.Normalize(oldPath); string fullOldPath = ResolveFullPath(PathTools.Normalize(oldPath));
newPath = PathTools.Normalize(newPath); string fullNewPath = ResolveFullPath(PathTools.Normalize(newPath));
return ParentFileSystem.RenameFile(oldPath, newPath); return ParentFileSystem.RenameFile(fullOldPath, fullNewPath);
} }
public Result GetEntryType(out DirectoryEntryType entryType, string path) public Result GetEntryType(out DirectoryEntryType entryType, string path)
{ {
path = PathTools.Normalize(path); string fullPath = ResolveFullPath(PathTools.Normalize(path));
return ParentFileSystem.GetEntryType(out entryType, ResolveFullPath(path)); return ParentFileSystem.GetEntryType(out entryType, fullPath);
} }
public Result Commit() public Result Commit()
@ -105,30 +105,30 @@ namespace LibHac.FsSystem
public Result GetFreeSpaceSize(out long freeSpace, string path) public Result GetFreeSpaceSize(out long freeSpace, string path)
{ {
path = PathTools.Normalize(path); string fullPath = ResolveFullPath(PathTools.Normalize(path));
return ParentFileSystem.GetFreeSpaceSize(out freeSpace, ResolveFullPath(path)); return ParentFileSystem.GetFreeSpaceSize(out freeSpace, fullPath);
} }
public Result GetTotalSpaceSize(out long totalSpace, string path) public Result GetTotalSpaceSize(out long totalSpace, string path)
{ {
path = PathTools.Normalize(path); string fullPath = ResolveFullPath(PathTools.Normalize(path));
return ParentFileSystem.GetTotalSpaceSize(out totalSpace, ResolveFullPath(path)); return ParentFileSystem.GetTotalSpaceSize(out totalSpace, fullPath);
} }
public Result GetFileTimeStampRaw(out FileTimeStampRaw timeStamp, string path) public Result GetFileTimeStampRaw(out FileTimeStampRaw timeStamp, string path)
{ {
path = PathTools.Normalize(path); string fullPath = ResolveFullPath(PathTools.Normalize(path));
return ParentFileSystem.GetFileTimeStampRaw(out timeStamp, ResolveFullPath(path)); return ParentFileSystem.GetFileTimeStampRaw(out timeStamp, fullPath);
} }
public Result QueryEntry(Span<byte> outBuffer, ReadOnlySpan<byte> inBuffer, QueryId queryId, string path) public Result QueryEntry(Span<byte> outBuffer, ReadOnlySpan<byte> inBuffer, QueryId queryId, string path)
{ {
path = PathTools.Normalize(path); string fullPath = ResolveFullPath(PathTools.Normalize(path));
return ParentFileSystem.QueryEntry(outBuffer, inBuffer, queryId, ResolveFullPath(path)); return ParentFileSystem.QueryEntry(outBuffer, inBuffer, queryId, fullPath);
} }
} }
} }