From d7f3e94577bcc8d3129aa7ed84b6c60727d69127 Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Tue, 1 Oct 2019 16:31:21 -0500 Subject: [PATCH] Add a basic SaveDataFileSystemCreator --- src/LibHac/Fs/FileSystemClient.AccessLog.cs | 2 +- src/LibHac/Fs/IAttributeFileSystem.cs | 5 +- src/LibHac/Fs/ResultFs.cs | 1 + src/LibHac/Fs/SaveData.cs | 11 ++- src/LibHac/Fs/UserId.cs | 2 + .../FsService/Creators/FileSystemCreators.cs | 1 + .../Creators/SaveDataFileSystemCreator.cs | 70 +++++++++++++++++++ src/LibHac/FsService/FileSystemProxy.cs | 2 +- .../FsSystem/ConcatenationFileSystem.cs | 24 +++---- .../FsSystem/DirectorySaveDataFileSystem.cs | 2 +- src/LibHac/FsSystem/FileSystemExtensions.cs | 12 +++- src/LibHac/FsSystem/LocalFileSystem.cs | 56 ++++++++++++--- src/LibHac/FsSystem/SubdirectoryFileSystem.cs | 64 ++++++++--------- 13 files changed, 188 insertions(+), 64 deletions(-) create mode 100644 src/LibHac/FsService/Creators/SaveDataFileSystemCreator.cs diff --git a/src/LibHac/Fs/FileSystemClient.AccessLog.cs b/src/LibHac/Fs/FileSystemClient.AccessLog.cs index 0df0f797..e4f3bee8 100644 --- a/src/LibHac/Fs/FileSystemClient.AccessLog.cs +++ b/src/LibHac/Fs/FileSystemClient.AccessLog.cs @@ -189,7 +189,7 @@ namespace LibHac.Fs rc = operation(); TimeSpan endTime = Time.GetCurrent(); - OutputAccessLog(rc, startTime, endTime, textGenerator(), caller); + OutputAccessLog(rc, startTime, endTime, handle, textGenerator(), caller); } else { diff --git a/src/LibHac/Fs/IAttributeFileSystem.cs b/src/LibHac/Fs/IAttributeFileSystem.cs index e5a4858d..676a266e 100644 --- a/src/LibHac/Fs/IAttributeFileSystem.cs +++ b/src/LibHac/Fs/IAttributeFileSystem.cs @@ -2,8 +2,9 @@ { public interface IAttributeFileSystem : IFileSystem { - NxFileAttributes GetFileAttributes(string path); - void SetFileAttributes(string path, NxFileAttributes attributes); + Result CreateDirectory(string path, NxFileAttributes archiveAttribute); + Result GetFileAttributes(string path, out NxFileAttributes attributes); + Result SetFileAttributes(string path, NxFileAttributes attributes); long GetFileSize(string path); } } diff --git a/src/LibHac/Fs/ResultFs.cs b/src/LibHac/Fs/ResultFs.cs index fcc8eb68..7d547155 100644 --- a/src/LibHac/Fs/ResultFs.cs +++ b/src/LibHac/Fs/ResultFs.cs @@ -27,6 +27,7 @@ public static Result InvalidIndirectStorageSource => new Result(ModuleFs, 4023); 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 Result4362 => new Result(ModuleFs, 4362); public static Result Result4363 => new Result(ModuleFs, 4363); diff --git a/src/LibHac/Fs/SaveData.cs b/src/LibHac/Fs/SaveData.cs index c9f0c4b4..09d98fb2 100644 --- a/src/LibHac/Fs/SaveData.cs +++ b/src/LibHac/Fs/SaveData.cs @@ -5,6 +5,11 @@ namespace LibHac.Fs { 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, 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, 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, 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, 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) diff --git a/src/LibHac/Fs/UserId.cs b/src/LibHac/Fs/UserId.cs index 4a14fc46..5d89fbcb 100644 --- a/src/LibHac/Fs/UserId.cs +++ b/src/LibHac/Fs/UserId.cs @@ -7,6 +7,8 @@ namespace LibHac.Fs [StructLayout(LayoutKind.Sequential, Size = 0x10)] public struct UserId : IEquatable, IComparable, IComparable { + public static readonly UserId EmptyId = new UserId(0, 0); + public readonly Id128 Id; public UserId(ulong high, ulong low) diff --git a/src/LibHac/FsService/Creators/FileSystemCreators.cs b/src/LibHac/FsService/Creators/FileSystemCreators.cs index 2afa63df..c0f83871 100644 --- a/src/LibHac/FsService/Creators/FileSystemCreators.cs +++ b/src/LibHac/FsService/Creators/FileSystemCreators.cs @@ -26,6 +26,7 @@ namespace LibHac.FsService.Creators var creators = new FileSystemCreators(); creators.SubDirectoryFileSystemCreator = new SubDirectoryFileSystemCreator(); + creators.SaveDataFileSystemCreator = new SaveDataFileSystemCreator(keyset); creators.EncryptedFileSystemCreator = new EncryptedFileSystemCreator(keyset); creators.BuiltInStorageFileSystemCreator = new EmulatedBisFileSystemCreator(rootFileSystem); creators.SdFileSystemCreator = new EmulatedSdFileSystemCreator(rootFileSystem); diff --git a/src/LibHac/FsService/Creators/SaveDataFileSystemCreator.cs b/src/LibHac/FsService/Creators/SaveDataFileSystemCreator.cs new file mode 100644 index 00000000..9574b70c --- /dev/null +++ b/src/LibHac/FsService/Creators/SaveDataFileSystemCreator.cs @@ -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 seed) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/LibHac/FsService/FileSystemProxy.cs b/src/LibHac/FsService/FileSystemProxy.cs index f4b8747f..0202cd03 100644 --- a/src/LibHac/FsService/FileSystemProxy.cs +++ b/src/LibHac/FsService/FileSystemProxy.cs @@ -163,7 +163,7 @@ namespace LibHac.FsService 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) { diff --git a/src/LibHac/FsSystem/ConcatenationFileSystem.cs b/src/LibHac/FsSystem/ConcatenationFileSystem.cs index f9683629..5de0cf13 100644 --- a/src/LibHac/FsSystem/ConcatenationFileSystem.cs +++ b/src/LibHac/FsSystem/ConcatenationFileSystem.cs @@ -54,14 +54,20 @@ namespace LibHac.FsSystem #if CROSS_PLATFORM 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 { return IsConcatenationFileHeuristic(path); } #else - return HasConcatenationFileAttribute(BaseFileSystem.GetFileAttributes(path)); + Result rc = BaseFileSystem.GetFileAttributes(path, out NxFileAttributes attributes); + if (rc.IsFailure()) return false; + + return HasConcatenationFileAttribute(attributes); #endif } @@ -93,11 +99,9 @@ namespace LibHac.FsSystem return (attributes & NxFileAttributes.Directory) != 0 && (attributes & NxFileAttributes.Archive) != 0; } - private void SetConcatenationFileAttribute(string path) + private Result SetConcatenationFileAttribute(string path) { - NxFileAttributes attributes = BaseFileSystem.GetFileAttributes(path); - attributes |= NxFileAttributes.Archive; - BaseFileSystem.SetFileAttributes(path, attributes); + return BaseFileSystem.SetFileAttributes(path, NxFileAttributes.Archive); } public Result CreateDirectory(string path) @@ -134,11 +138,9 @@ namespace LibHac.FsSystem return ResultFs.PathNotFound.Log(); } - Result rc = BaseFileSystem.CreateDirectory(path); + Result rc = BaseFileSystem.CreateDirectory(path, NxFileAttributes.Archive); if (rc.IsFailure()) return rc; - SetConcatenationFileAttribute(path); - long remaining = size; for (int i = 0; remaining > 0; i++) @@ -320,9 +322,7 @@ namespace LibHac.FsSystem { if (queryId != QueryId.MakeConcatFile) return ResultFs.UnsupportedOperationInConcatFsQueryEntry.Log(); - SetConcatenationFileAttribute(path); - - return Result.Success; + return SetConcatenationFileAttribute(path); } private int GetSubFileCount(string dirPath) diff --git a/src/LibHac/FsSystem/DirectorySaveDataFileSystem.cs b/src/LibHac/FsSystem/DirectorySaveDataFileSystem.cs index 361f6ce7..4a5838fa 100644 --- a/src/LibHac/FsSystem/DirectorySaveDataFileSystem.cs +++ b/src/LibHac/FsSystem/DirectorySaveDataFileSystem.cs @@ -213,7 +213,7 @@ namespace LibHac.FsSystem rc = BaseFs.CreateDirectory(dest); if (rc.IsFailure()) return rc; - return this.CopyDirectory(this, src, dest); + return BaseFs.CopyDirectory(BaseFs, src, dest); } internal void NotifyCloseWritableFile() diff --git a/src/LibHac/FsSystem/FileSystemExtensions.cs b/src/LibHac/FsSystem/FileSystemExtensions.cs index 1c89dd2c..cbe7478d 100644 --- a/src/LibHac/FsSystem/FileSystemExtensions.cs +++ b/src/LibHac/FsSystem/FileSystemExtensions.cs @@ -182,10 +182,18 @@ namespace LibHac.FsSystem 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) { - var nxAttributeBits = (FileAttributes)(((int)nxAttributes & 3) << 4); - return attributes | nxAttributeBits; + // The only 2 bits from FileAttributes that are used in NxFileAttributes + const int mask = 3 << 4; + + FileAttributes oldAttributes = attributes & (FileAttributes)mask; + return oldAttributes | nxAttributes.ToFatAttributes(); } public static void SetConcatenationFileAttribute(this IFileSystem fs, string path) diff --git a/src/LibHac/FsSystem/LocalFileSystem.cs b/src/LibHac/FsSystem/LocalFileSystem.cs index 0e3431e5..13d39665 100644 --- a/src/LibHac/FsSystem/LocalFileSystem.cs +++ b/src/LibHac/FsSystem/LocalFileSystem.cs @@ -34,21 +34,46 @@ namespace LibHac.FsSystem return PathTools.Combine(BasePath, path); } - public NxFileAttributes GetFileAttributes(string path) + public Result GetFileAttributes(string path, out NxFileAttributes attributes) { - path = PathTools.Normalize(path); - return File.GetAttributes(ResolveLocalPath(path)).ToNxAttributes(); + string localPath = ResolveLocalPath(PathTools.Normalize(path)); + + 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(path); + string localPath = ResolveLocalPath(PathTools.Normalize(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); - File.SetAttributes(localPath, attributesNew); + try + { + info.Attributes = attributesNew; + } + catch (IOException) + { + return ResultFs.PathNotFound.Log(); + } + + return Result.Success; } public long GetFileSize(string path) @@ -60,6 +85,11 @@ namespace LibHac.FsSystem } public Result CreateDirectory(string path) + { + return CreateDirectory(path, NxFileAttributes.None); + } + + public Result CreateDirectory(string path, NxFileAttributes archiveAttribute) { string localPath = ResolveLocalPath(PathTools.Normalize(path)); @@ -75,7 +105,7 @@ namespace LibHac.FsSystem return ResultFs.PathNotFound.Log(); } - return CreateDirInternal(dir); + return CreateDirInternal(dir, archiveAttribute); } public Result CreateFile(string path, long size, CreateFileOptions options) @@ -394,11 +424,17 @@ namespace LibHac.FsSystem return Result.Success; } - private static Result CreateDirInternal(DirectoryInfo dir) + private static Result CreateDirInternal(DirectoryInfo dir, NxFileAttributes attributes) { try { dir.Create(); + dir.Refresh(); + + if (attributes.HasFlag(NxFileAttributes.Archive)) + { + dir.Attributes |= FileAttributes.Archive; + } } catch (DirectoryNotFoundException) { diff --git a/src/LibHac/FsSystem/SubdirectoryFileSystem.cs b/src/LibHac/FsSystem/SubdirectoryFileSystem.cs index ad61357e..e9bb51f8 100644 --- a/src/LibHac/FsSystem/SubdirectoryFileSystem.cs +++ b/src/LibHac/FsSystem/SubdirectoryFileSystem.cs @@ -21,81 +21,81 @@ namespace LibHac.FsSystem 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) { - 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) { - path = PathTools.Normalize(path); + string fullPath = ResolveFullPath(PathTools.Normalize(path)); - return ParentFileSystem.DeleteDirectory(ResolveFullPath(path)); + return ParentFileSystem.DeleteDirectory(fullPath); } 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) { - path = PathTools.Normalize(path); + string fullPath = ResolveFullPath(PathTools.Normalize(path)); - return ParentFileSystem.CleanDirectoryRecursively(ResolveFullPath(path)); + return ParentFileSystem.CleanDirectoryRecursively(fullPath); } 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) { - 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) { - 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) { - oldPath = PathTools.Normalize(oldPath); - newPath = PathTools.Normalize(newPath); + string fullOldPath = ResolveFullPath(PathTools.Normalize(oldPath)); + string fullNewPath = ResolveFullPath(PathTools.Normalize(newPath)); - return ParentFileSystem.RenameDirectory(oldPath, newPath); + return ParentFileSystem.RenameDirectory(fullOldPath, fullNewPath); } public Result RenameFile(string oldPath, string newPath) { - oldPath = PathTools.Normalize(oldPath); - newPath = PathTools.Normalize(newPath); + string fullOldPath = ResolveFullPath(PathTools.Normalize(oldPath)); + string fullNewPath = ResolveFullPath(PathTools.Normalize(newPath)); - return ParentFileSystem.RenameFile(oldPath, newPath); + return ParentFileSystem.RenameFile(fullOldPath, fullNewPath); } 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() @@ -105,30 +105,30 @@ namespace LibHac.FsSystem 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) { - 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) { - 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 outBuffer, ReadOnlySpan 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); } } }