private Result GetNcaSectionIndex(out int index, FileSystemProxyType fspType) { switch (fspType) { case FileSystemProxyType.Code: case FileSystemProxyType.Control: case FileSystemProxyType.Manual: case FileSystemProxyType.Meta: case FileSystemProxyType.Data: index = 0; return(Result.Success); case FileSystemProxyType.Rom: case FileSystemProxyType.RegisteredUpdate: index = 1; return(Result.Success); case FileSystemProxyType.Logo: index = 2; return(Result.Success); default: index = default; return(ResultFs.InvalidArgument.Log()); } }
private Result OpenSubDirectoryForFsType(out IFileSystem fileSystem, IFileSystem baseFileSystem, FileSystemProxyType fsType) { fileSystem = default; ReadOnlySpan <byte> dirName; // Get the name of the subdirectory for the filesystem type switch (fsType) { case FileSystemProxyType.Package: fileSystem = baseFileSystem; return(Result.Success); case FileSystemProxyType.Code: dirName = new[] { (byte)'/', (byte)'c', (byte)'o', (byte)'d', (byte)'e', (byte)'/' }; break; case FileSystemProxyType.Rom: case FileSystemProxyType.Control: case FileSystemProxyType.Manual: case FileSystemProxyType.Meta: // Nintendo doesn't include the Data case in the switch. Maybe an oversight? case FileSystemProxyType.Data: case FileSystemProxyType.RegisteredUpdate: dirName = new[] { (byte)'/', (byte)'d', (byte)'a', (byte)'t', (byte)'a', (byte)'/' }; break; case FileSystemProxyType.Logo: dirName = new[] { (byte)'/', (byte)'l', (byte)'o', (byte)'g', (byte)'o', (byte)'/' }; break; default: return(ResultFs.InvalidArgument.Log()); } // Open the subdirectory filesystem Result rc = FsCreators.SubDirectoryFileSystemCreator.Create(out IFileSystem subDirFs, baseFileSystem, new U8Span(dirName)); if (rc.IsFailure()) { return(rc); } if (fsType == FileSystemProxyType.Code) { rc = FsCreators.StorageOnNcaCreator.VerifyAcidSignature(subDirFs, null); if (rc.IsFailure()) { return(rc); } } fileSystem = subDirFs; return(Result.Success); }
public static Result MountContent(this FileSystemClient fs, U8Span mountName, U8Span path, TitleId titleId, ContentType type) { Result rc = MountHelpers.CheckMountNameAcceptingReservedMountName(mountName); if (rc.IsFailure()) { return(rc); } FileSystemProxyType fspType = ConvertToFileSystemProxyType(type); return(MountContentImpl(fs, mountName, path, titleId, fspType)); }
private Result TryOpenContentDirectory(U8Span path, out IFileSystem contentFileSystem, IFileSystem baseFileSystem, FileSystemProxyType fsType, bool preserveUnc) { contentFileSystem = default; FsPath fullPath; unsafe { _ = &fullPath; } // workaround for CS0165 Result rc = FsCreators.SubDirectoryFileSystemCreator.Create(out IFileSystem subDirFs, baseFileSystem, path, preserveUnc); if (rc.IsFailure()) { return(rc); } return(OpenSubDirectoryForFsType(out contentFileSystem, subDirFs, fsType)); }
private static Result MountContentImpl(FileSystemClient fs, U8Span mountName, U8Span path, ulong id, ContentType contentType) { Result rc = fs.Impl.CheckMountNameAcceptingReservedMountName(mountName); if (rc.IsFailure()) { return(rc); } FileSystemProxyType fsType = ConvertToFileSystemProxyType(contentType); if (path.IsNull()) { return(ResultFs.NullptrArgument.Log()); } rc = FspPath.FromSpan(out FspPath fsPath, path); if (rc.IsFailure()) { return(rc); } using ReferenceCountedDisposable <IFileSystemProxy> fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); ReferenceCountedDisposable <IFileSystemSf> fileSystem = null; try { rc = fsProxy.Target.OpenFileSystemWithId(out fileSystem, in fsPath, id, fsType); if (rc.IsFailure()) { return(rc); } var fileSystemAdapter = new FileSystemServiceObjectAdapter(fileSystem); return(fs.Register(mountName, fileSystemAdapter)); } finally { fileSystem?.Dispose(); } }
public static Result MountContent(this FileSystemClient fs, U8Span mountName, TitleId programId, ContentType type) { Result rc = MountHelpers.CheckMountNameAcceptingReservedMountName(mountName); if (rc.IsFailure()) { return(rc); } FileSystemProxyType fspType = ConvertToFileSystemProxyType(type); IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject(); rc = fsProxy.OpenFileSystemWithPatch(out IFileSystem fileSystem, programId, fspType); if (rc.IsFailure()) { return(rc); } return(fs.Register(mountName, fileSystem)); }
private Result OpenNcaStorage(out IStorage ncaStorage, Nca nca, out NcaFormatType fsType, FileSystemProxyType fsProxyType, bool isGameCard, bool canMountSystemDataPrivate) { ncaStorage = default; fsType = default; NcaContentType contentType = nca.Header.ContentType; switch (fsProxyType) { case FileSystemProxyType.Code: case FileSystemProxyType.Rom: case FileSystemProxyType.Logo: case FileSystemProxyType.RegisteredUpdate: if (contentType != NcaContentType.Program) { return(ResultFs.PreconditionViolation.Log()); } break; case FileSystemProxyType.Control: if (contentType != NcaContentType.Control) { return(ResultFs.PreconditionViolation.Log()); } break; case FileSystemProxyType.Manual: if (contentType != NcaContentType.Manual) { return(ResultFs.PreconditionViolation.Log()); } break; case FileSystemProxyType.Meta: if (contentType != NcaContentType.Meta) { return(ResultFs.PreconditionViolation.Log()); } break; case FileSystemProxyType.Data: if (contentType != NcaContentType.Data && contentType != NcaContentType.PublicData) { return(ResultFs.PreconditionViolation.Log()); } if (contentType == NcaContentType.Data && !canMountSystemDataPrivate) { return(ResultFs.PermissionDenied.Log()); } break; default: return(ResultFs.InvalidArgument.Log()); } if (nca.Header.DistributionType == DistributionType.GameCard && !isGameCard) { return(ResultFs.PermissionDenied.Log()); } Result rc = SetNcaExternalKey(nca); if (rc.IsFailure()) { return(rc); } rc = GetNcaSectionIndex(out int sectionIndex, fsProxyType); if (rc.IsFailure()) { return(rc); } rc = FsCreators.StorageOnNcaCreator.Create(out ncaStorage, out NcaFsHeader fsHeader, nca, sectionIndex, fsProxyType == FileSystemProxyType.Code); if (rc.IsFailure()) { return(rc); } fsType = fsHeader.FormatType; return(Result.Success); }
public Result OpenFileSystem(out IFileSystem fileSystem, U8Span path, FileSystemProxyType type, bool canMountSystemDataPrivate, ulong programId) { fileSystem = default; // Get a reference to the path that will be advanced as each part of the path is parsed U8Span currentPath = path.Slice(0, StringUtils.GetLength(path)); // Open the root filesystem based on the path's mount name Result rc = OpenFileSystemFromMountName(ref currentPath, out IFileSystem baseFileSystem, out bool shouldContinue, out MountNameInfo mountNameInfo); if (rc.IsFailure()) { return(rc); } // Don't continue if the rest of the path is empty if (!shouldContinue) { return(ResultFs.InvalidArgument.Log()); } if (type == FileSystemProxyType.Logo && mountNameInfo.IsGameCard) { rc = OpenGameCardFileSystem(out fileSystem, new GameCardHandle(mountNameInfo.GcHandle), GameCardPartition.Logo); if (rc.IsSuccess()) { return(Result.Success); } if (!ResultFs.PartitionNotFound.Includes(rc)) { return(rc); } } rc = IsContentPathDir(ref currentPath, out bool isDirectory); if (rc.IsFailure()) { return(rc); } if (isDirectory) { if (!mountNameInfo.IsHostFs) { return(ResultFs.PermissionDenied.Log()); } if (type == FileSystemProxyType.Manual) { rc = TryOpenCaseSensitiveContentDirectory(out IFileSystem manualFileSystem, baseFileSystem, currentPath); if (rc.IsFailure()) { return(rc); } fileSystem = new ReadOnlyFileSystem(manualFileSystem); return(Result.Success); } return(TryOpenContentDirectory(currentPath, out fileSystem, baseFileSystem, type, true)); } rc = TryOpenNsp(ref currentPath, out IFileSystem nspFileSystem, baseFileSystem); if (rc.IsSuccess()) { // Must be the end of the path to open Application Package FS type if (currentPath.Length == 0 || currentPath[0] == 0) { if (type == FileSystemProxyType.Package) { fileSystem = nspFileSystem; return(Result.Success); } return(ResultFs.InvalidArgument.Log()); } baseFileSystem = nspFileSystem; } if (!mountNameInfo.CanMountNca) { return(ResultFs.InvalidNcaMountPoint.Log()); } ulong openProgramId = mountNameInfo.IsHostFs ? ulong.MaxValue : programId; rc = TryOpenNca(ref currentPath, out Nca nca, baseFileSystem, openProgramId); if (rc.IsFailure()) { return(rc); } rc = OpenNcaStorage(out IStorage ncaSectionStorage, nca, out NcaFormatType fsType, type, mountNameInfo.IsGameCard, canMountSystemDataPrivate); if (rc.IsFailure()) { return(rc); } switch (fsType) { case NcaFormatType.Romfs: return(FsCreators.RomFileSystemCreator.Create(out fileSystem, ncaSectionStorage)); case NcaFormatType.Pfs0: return(FsCreators.PartitionFileSystemCreator.Create(out fileSystem, ncaSectionStorage)); default: return(ResultFs.InvalidNcaFsType.Log()); } }
public Result OpenFileSystemWithPatch(out IFileSystem fileSystem, TitleId titleId, FileSystemProxyType type) { throw new NotImplementedException(); }
public Result OpenFileSystemWithId(out IFileSystem fileSystem, ref FsPath path, TitleId titleId, FileSystemProxyType type) { fileSystem = default; // Missing permission check, speed emulation storage type wrapper, and FileSystemInterfaceAdapter bool canMountSystemDataPrivate = false; var normalizer = new PathNormalizer(path, GetPathNormalizerOptions(path)); if (normalizer.Result.IsFailure()) { return(normalizer.Result); } // ReSharper disable once ConditionIsAlwaysTrueOrFalse return(FsProxyCore.OpenFileSystem(out fileSystem, normalizer.Path, type, canMountSystemDataPrivate, titleId)); }
public Result OpenFileSystemWithPatch(out ReferenceCountedDisposable <IFileSystemSf> fileSystem, ProgramId programId, FileSystemProxyType fsType) { UnsafeHelpers.SkipParamInit(out fileSystem); const StorageType storageFlag = StorageType.All; using var scopedLayoutType = new ScopedStorageLayoutTypeSetter(storageFlag); // Get the program info for the caller and verify permissions Result rc = GetProgramInfo(out ProgramInfo callerProgramInfo); if (rc.IsFailure()) { return(rc); } if (fsType != FileSystemProxyType.Manual) { if (fsType == FileSystemProxyType.Logo || fsType == FileSystemProxyType.Control) { return(ResultFs.NotImplemented.Log()); } else { return(ResultFs.InvalidArgument.Log()); } } Accessibility accessibility = callerProgramInfo.AccessControl.GetAccessibilityFor(AccessibilityType.MountContentManual); if (!accessibility.CanRead) { return(ResultFs.PermissionDenied.Log()); } // Get the program info for the owner of the file system being opened rc = GetProgramInfoByProgramId(out ProgramInfo ownerProgramInfo, programId.Value); if (rc.IsFailure()) { return(rc); } // Try to find the path to the original version of the file system Result originalResult = ServiceImpl.ResolveApplicationHtmlDocumentPath(out Path originalPath, new Ncm.ApplicationId(programId.Value), ownerProgramInfo.StorageId); // The file system might have a patch version with no original version, so continue if not found if (originalResult.IsFailure() && !ResultLr.HtmlDocumentNotFound.Includes(originalResult)) { return(originalResult); } // Use a separate bool because ref structs can't be used as type parameters bool originalPathNormalizerHasValue = false; PathNormalizer originalPathNormalizer = default; // Normalize the original version path if found if (originalResult.IsSuccess()) { originalPathNormalizer = new PathNormalizer(originalPath, GetPathNormalizerOptions(originalPath)); if (originalPathNormalizer.Result.IsFailure()) { return(originalPathNormalizer.Result); } originalPathNormalizerHasValue = true; } // Try to find the path to the patch file system Result patchResult = ServiceImpl.ResolveRegisteredHtmlDocumentPath(out Path patchPath, programId.Value); ReferenceCountedDisposable <IFileSystem> tempFileSystem = null; ReferenceCountedDisposable <IRomFileSystemAccessFailureManager> accessFailureManager = null; try { if (ResultLr.HtmlDocumentNotFound.Includes(patchResult)) { // There must either be an original version or patch version of the file system being opened if (originalResult.IsFailure()) { return(originalResult); } Assert.True(originalPathNormalizerHasValue); // There is an original version and no patch version. Open the original directly rc = ServiceImpl.OpenFileSystem(out tempFileSystem, originalPathNormalizer.Path, fsType, programId.Value); if (rc.IsFailure()) { return(rc); } } else { // Get the normalized path to the original file system U8Span normalizedOriginalPath; if (originalPathNormalizerHasValue) { normalizedOriginalPath = originalPathNormalizer.Path; } else { normalizedOriginalPath = U8Span.Empty; } // Normalize the path to the patch file system var patchPathNormalizer = new PathNormalizer(patchPath, GetPathNormalizerOptions(patchPath)); if (patchPathNormalizer.Result.IsFailure()) { return(patchPathNormalizer.Result); } if (patchResult.IsFailure()) { return(patchResult); } U8Span normalizedPatchPath = patchPathNormalizer.Path; // Open the file system using both the original and patch versions rc = ServiceImpl.OpenFileSystemWithPatch(out tempFileSystem, normalizedOriginalPath, normalizedPatchPath, fsType, programId.Value); if (rc.IsFailure()) { return(rc); } } // Add all the file system wrappers tempFileSystem = StorageLayoutTypeSetFileSystem.CreateShared(ref tempFileSystem, storageFlag); tempFileSystem = AsynchronousAccessFileSystem.CreateShared(ref tempFileSystem); accessFailureManager = SelfReference.AddReference <IRomFileSystemAccessFailureManager>(); tempFileSystem = DeepRetryFileSystem.CreateShared(ref tempFileSystem, ref accessFailureManager); fileSystem = FileSystemInterfaceAdapter.CreateShared(ref tempFileSystem); return(Result.Success); } finally { tempFileSystem?.Dispose(); accessFailureManager?.Dispose(); } }
private static Result MountContentImpl(FileSystemClient fs, U8Span mountName, U8Span path, TitleId titleId, FileSystemProxyType type) { FsPath.FromSpan(out FsPath fsPath, path); IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject(); Result rc = fsProxy.OpenFileSystemWithId(out IFileSystem fileSystem, ref fsPath, titleId, type); if (rc.IsFailure()) { return(rc); } return(fs.Register(mountName, fileSystem)); }