// ReSharper disable once UnusedParameter.Local private static Result MountBisImpl(FileSystemClient fs, U8Span mountName, BisPartitionId partitionId, U8Span rootPath) { Result rc = MountHelpers.CheckMountNameAcceptingReservedMountName(mountName); if (rc.IsFailure()) { return(rc); } FsPath sfPath; unsafe { _ = &sfPath; } // workaround for CS0165 IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject(); // Nintendo doesn't use the provided rootPath sfPath.Str[0] = 0; rc = fsProxy.OpenBisFileSystem(out IFileSystem fileSystem, ref sfPath, partitionId); if (rc.IsFailure()) { return(rc); } var nameGenerator = new BisCommonMountNameGenerator(partitionId); return(fs.Register(mountName, fileSystem, nameGenerator)); }
public static Result MountSystemSaveData(this FileSystemClient fs, U8Span mountName, SaveDataSpaceId spaceId, ulong saveDataId, UserId userId) { Result rc = MountHelpers.CheckMountName(mountName); if (rc.IsFailure()) { return(rc); } IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject(); SaveDataAttribute attribute = default; attribute.UserId = userId; attribute.SaveDataId = saveDataId; rc = fsProxy.OpenSaveDataFileSystemBySystemSaveDataId(out IFileSystem fileSystem, spaceId, ref attribute); if (rc.IsFailure()) { return(rc); } return(fs.Register(mountName, fileSystem)); }
private static Result MountCodeImpl(this FileSystemClient fs, out CodeVerificationData verificationData, U8Span mountName, U8Span path, ProgramId programId) { Unsafe.SkipInit(out verificationData); Result rc = MountHelpers.CheckMountName(mountName); if (rc.IsFailure()) { return(rc); } rc = FspPath.FromSpan(out FspPath fsPath, path); if (rc.IsFailure()) { return(rc); } IFileSystemProxyForLoader fsProxy = fs.GetFileSystemProxyForLoaderServiceObject(); rc = fsProxy.OpenCodeFileSystem(out IFileSystem codeFs, out verificationData, in fsPath, programId); if (rc.IsFailure()) { return(rc); } return(fs.Register(mountName, codeFs)); }
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)); }
static Result Run(FileSystemClient fs, U8Span mountName, U8Span path) { // ReSharper disable once VariableHidesOuterVariable Result rc = MountHelpers.CheckMountName(mountName); if (rc.IsFailure()) { return(rc); } FsPath.FromSpan(out FsPath fsPath, path); IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject(); rc = fsProxy.OpenFileSystemWithId(out IFileSystem fileSystem, ref fsPath, default, FileSystemProxyType.Package);
public static Result MountCustomStorage(this FileSystemClient fs, U8Span mountName, CustomStorageId storageId) { Result rc = MountHelpers.CheckMountName(mountName); if (rc.IsFailure()) { return(rc); } IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject(); rc = fsProxy.OpenCustomStorageFileSystem(out IFileSystem customFs, storageId); if (rc.IsFailure()) { return(rc); } return(fs.Register(mountName, customFs)); }
static Result Run(FileSystemClient fs, U8Span mountName) { // ReSharper disable once VariableHidesOuterVariable Result rc = MountHelpers.CheckMountName(mountName); if (rc.IsFailure()) { return(rc); } IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject(); rc = fsProxy.OpenSdCardFileSystem(out IFileSystem fileSystem); if (rc.IsFailure()) { return(rc); } return(fs.Register(mountName, fileSystem)); }
// Drop the in-bottle hostname. internal static void DropHostname(bool verbose) { // Drop the in-bottle hostname. if (verbose) { Console.WriteLine("genie: dropping in-bottle hostname"); } if (!MountHelpers.UnMount("/etc/hostname")) { Console.WriteLine("genie: shutdown failed; unmounting hostname"); Environment.Exit(EPERM); } File.Delete("/run/hostname-wsl"); var hostname = File.ReadAllLines("/etc/hostname")[0].TrimEnd(); HostHelpers.Hostname = hostname; }
public static Result MountContentStorage(this FileSystemClient fs, U8Span mountName, ContentStorageId storageId) { Result rc = MountHelpers.CheckMountNameAcceptingReservedMountName(mountName); if (rc.IsFailure()) { return(rc); } IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject(); rc = fsProxy.OpenContentStorageFileSystem(out IFileSystem contentFs, storageId); if (rc.IsFailure()) { return(rc); } var mountNameGenerator = new ContentStorageCommonMountNameGenerator(storageId); return(fs.Register(mountName, contentFs, mountNameGenerator)); }
private static Result MountBcatSaveDataImpl(FileSystemClient fs, U8Span mountName, Ncm.ApplicationId applicationId) { Result rc = MountHelpers.CheckMountName(mountName); if (rc.IsFailure()) { return(rc); } IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject(); var attribute = new SaveDataAttribute(applicationId, SaveDataType.Bcat, UserId.Zero, 0); rc = fsProxy.OpenSaveDataFileSystem(out IFileSystem fileSystem, SaveDataSpaceId.User, ref attribute); if (rc.IsFailure()) { return(rc); } return(fs.Register(mountName, fileSystem)); }
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)); }
public static Result MountSystemSaveData(this FileSystemClient fs, U8Span mountName, SaveDataSpaceId spaceId, ulong saveDataId, UserId userId) { Result rc = MountHelpers.CheckMountName(mountName); if (rc.IsFailure()) { return(rc); } IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject(); var attribute = new SaveDataAttribute(ProgramId.InvalidId, SaveDataType.System, userId, saveDataId); rc = fsProxy.OpenSaveDataFileSystemBySystemSaveDataId(out IFileSystem fileSystem, spaceId, ref attribute); if (rc.IsFailure()) { return(rc); } return(fs.Register(mountName, fileSystem)); }
public static Result MountGameCardPartition(this FileSystemClient fs, U8Span mountName, GameCardHandle handle, GameCardPartition partitionId) { Result rc = MountHelpers.CheckMountNameAcceptingReservedMountName(mountName); if (rc.IsFailure()) { return(rc); } IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject(); rc = fsProxy.OpenGameCardFileSystem(out IFileSystem cardFs, handle, partitionId); if (rc.IsFailure()) { return(rc); } var mountNameGenerator = new GameCardCommonMountNameGenerator(handle, partitionId); return(fs.Register(mountName, cardFs, mountNameGenerator)); }
private static Result MountSaveDataImpl(this FileSystemClient fs, U8Span mountName, SaveDataSpaceId spaceId, TitleId titleId, UserId userId, SaveDataType type, bool openReadOnly, short index) { Result rc = MountHelpers.CheckMountName(mountName); if (rc.IsFailure()) { return(rc); } IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject(); SaveDataAttribute attribute = default; attribute.TitleId = titleId; attribute.UserId = userId; attribute.Type = type; attribute.Index = index; IFileSystem saveFs; if (openReadOnly) { rc = fsProxy.OpenReadOnlySaveDataFileSystem(out saveFs, spaceId, ref attribute); } else { rc = fsProxy.OpenSaveDataFileSystem(out saveFs, spaceId, ref attribute); } if (rc.IsFailure()) { return(rc); } return(fs.Register(mountName, saveFs)); }
private static Result MountBcatSaveDataImpl(FileSystemClient fs, U8Span mountName, TitleId applicationId) { Result rc = MountHelpers.CheckMountName(mountName); if (rc.IsFailure()) { return(rc); } IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject(); SaveDataAttribute attribute = default; attribute.TitleId = applicationId; attribute.Type = SaveDataType.Bcat; rc = fsProxy.OpenSaveDataFileSystem(out IFileSystem fileSystem, SaveDataSpaceId.User, ref attribute); if (rc.IsFailure()) { return(rc); } return(fs.Register(mountName, fileSystem)); }
// Shut down the bottle and clean up. public static int ShutdownHandler(bool verbose) { // Get the bottle state. var state = GetBottleState(verbose); if (!state.existedAtStart) { Console.WriteLine("genie: no bottle exists."); return(EINVAL); } if (state.startedWithin) { Console.WriteLine("genie: cannot shut down bottle from inside bottle; exiting."); return(EINVAL); } using (var r = new RootPrivilege()) { if (verbose) { Console.WriteLine("genie: running systemctl poweroff within bottle"); } var systemdPid = Helpers.GetSystemdPid(); var sd = Process.GetProcessById(systemdPid); Helpers.Chain("nsenter", new string[] { "-t", systemdPid.ToString(), "-m", "-p", "systemctl", "poweroff" }, "running command failed; nsenter"); Console.Write("Waiting for systemd exit..."); // Wait for systemd to exit. int timeout = Config.SystemdStartupTimeout; while (!sd.WaitForExit(1000)) { Console.Write("."); timeout--; if (timeout < 0) { Console.WriteLine("\n\nTimed out waiting for systemd to exit.\nThis may indicate a systemd configuration error.\nAttempting to continue."); break; } } Console.WriteLine(); // Having unmounted the binfmts fs before starting systemd, we remount it now as // a courtesy. But remember, genie is not guaranteed to be idempotent, so don't // rely on this, for the love of Thompson and Ritchie! if (!Directory.Exists("/proc/sys/fs/binfmt_misc")) { if (verbose) { Console.WriteLine("genie: remounting binfmt_misc filesystem as a courtesy"); } if (!MountHelpers.Mount("binfmt_misc", "/proc/sys/fs/binfmt_misc", FsType.BinaryFormats)) { Console.WriteLine("genie: failed to remount binfmt_misc filesystem; attempting to continue"); } } if (Config.ResolvedStub) { Helpers.RemoveResolvSymlink(verbose); } if (Config.UpdateHostname) { Thread.Sleep(500); Helpers.DropHostname(verbose); } } return(0); }
// Do the work of initializing the bottle. private static void InitializeBottle(bool verbose) { if (verbose) { Console.WriteLine("genie: initializing bottle."); } // Create the path file. File.WriteAllText("/run/genie.path", originalPath); // Create the env file. File.WriteAllLines("/run/genie.env", clonedVariables); // Now that the WSL hostname can be set via .wslconfig, we're going to make changing // it automatically in genie an option, enable/disable in genie.ini. Defaults to on // for backwards compatibility. if (Config.UpdateHostname) { Helpers.UpdateHostname(Config.HostnameSuffix, verbose); } // If configured to, create the resolv.conf symlink. if (Config.ResolvedStub) { Helpers.CreateResolvSymlink(verbose); } // Unmount the binfmts fs before starting systemd, so systemd can mount it // again with all the trimmings. if (Directory.Exists("/proc/sys/fs/binfmt_misc")) { if (verbose) { Console.WriteLine("genie: unmounting binfmt_misc filesystem before proceeding"); } if (!MountHelpers.UnMount("/proc/sys/fs/binfmt_misc")) { Console.WriteLine("genie: failed to unmount binfmt_misc filesystem; attempting to continue"); } } else { if (verbose) { Console.WriteLine("genie: no binfmt_misc filesystem present"); } } // Define systemd startup chain - command string to pass to daemonize string [] startupChain = new string[] { Config.PathToUnshare, "-fp", "--propagation", "shared", "--mount-proc", "--" }; // Are we doing AppArmor? if (Config.AppArmorNamespace) { // Check whether AppArmor is available in the kernel. if (Directory.Exists("/sys/module/apparmor")) { // If the AppArmor filesystem is not mounted, mount it. if (!Directory.Exists("/sys/kernel/security/apparmor")) { // Mount AppArmor filesystem. if (!MountHelpers.Mount("securityfs", "/sys/kernel/security", "securityfs")) { Console.WriteLine("genie: could not mount AppArmor filesystem; attempting to continue without AppArmor namespace"); goto error; } } // Create AppArmor namespace for genie bottle string nsName = $"genie-{Helpers.WslDistroName}"; if (verbose) { Console.WriteLine($"genie: creating AppArmor namespace {nsName}"); } if (!Directory.Exists("/sys/kernel/security/apparmor/policy/namespaces")) { Console.WriteLine("genie: could not find AppArmor filesystem; attempting to continue without AppArmor namespace"); goto error; } Directory.CreateDirectory($"/sys/kernel/security/apparmor/policy/namespaces/{nsName}"); // Update startup chain with aa-exec command. startupChain = startupChain.Concat(new string[] { "aa-exec", "-n", $"{nsName}", "-p", "unconfined", "--" }).ToArray(); } else { Console.WriteLine("genie: AppArmor not available in kernel; attempting to continue without AppArmor namespace"); } } error: // Update startup chain with systemd command. startupChain = startupChain.Append("systemd").ToArray(); // Run systemd in a container. if (verbose) { Console.WriteLine("genie: starting systemd."); } Console.WriteLine(string.Join(" ", startupChain)); Helpers.Chain("daemonize", startupChain, "initializing bottle failed; daemonize"); // Wait for systemd to be up. (Polling, sigh.) Console.Write("Waiting for systemd..."); int systemdPid; do { Thread.Sleep(500); systemdPid = Helpers.GetSystemdPid(); Console.Write("."); } while (systemdPid == 0); // Now that systemd exists, write out its (external) PID. // We do not need to store the inside-bottle PID anywhere for obvious reasons. // Create the path file. File.WriteAllText("/run/genie.systemd.pid", systemdPid.ToString()); // Wait for systemd to be in running state. int runningYet = 255; int timeout = Config.SystemdStartupTimeout; var ryArgs = new string[] { "-c", $"nsenter -t {systemdPid} -m -p systemctl is-system-running -q 2> /dev/null" }; do { Thread.Sleep(1000); runningYet = Helpers.RunAndWait("sh", ryArgs); Console.Write("!"); timeout--; if (timeout < 0) { // What state are we in? var state = Helpers.RunAndWaitForOutput("sh", new string[] { "-c", $"\"nsenter -t {systemdPid} -m -p systemctl is-system-running 2> /dev/null\"" }); if (state.StartsWith("starting")) { Console.WriteLine("\n\nSystemd is still starting. This may indicate a unit is being slow to start.\nAttempting to continue.\n"); } else { Console.WriteLine("\n\nTimed out waiting for systemd to enter running state.\nThis may indicate a systemd configuration error.\nAttempting to continue.\nFailed units will now be displayed (systemctl list-units --failed):"); Helpers.Chain("nsenter", new string[] { "-t", systemdPid.ToString(), "-m", "-p", "systemctl", "list-units", "--failed" }, "running command failed; nsenter for systemctl list-units --failed"); } break; } } while (runningYet != 0); Console.WriteLine(); }
// Do the work of initializing the bottle. private static void InitializeBottle(bool verbose) { if (verbose) { Console.WriteLine("genie: initializing bottle."); } // Create the path file. File.WriteAllText("/run/genie.path", originalPath); // Create the env file. File.WriteAllLines("/run/genie.env", clonedVariables); // Now that the WSL hostname can be set via .wslconfig, we're going to make changing // it automatically in genie an option, enable/disable in genie.ini. Defaults to on // for backwards compatibility. if (Config.UpdateHostname) { Helpers.UpdateHostname(verbose); } // Unmount the binfmts fs before starting systemd, so systemd can mount it // again with all the trimmings. if (Directory.Exists("/proc/sys/fs/binfmt_misc")) { if (verbose) { Console.WriteLine("genie: unmounting binfmt_misc filesystem before proceeding"); } if (!MountHelpers.UnMount("/proc/sys/fs/binfmt_misc")) { Console.WriteLine("genie: failed to unmount binfmt_misc filesystem; attempting to continue"); } } else { if (verbose) { Console.WriteLine("genie: no binfmt_misc filesystem present"); } } // Run systemd in a container. if (verbose) { Console.WriteLine("genie: starting systemd."); } Helpers.Chain("daemonize", new string[] { Config.PathToUnshare, "-fp", "--propagation", "shared", "--mount-proc", "systemd" }, "initializing bottle failed; daemonize"); // Wait for systemd to be up. (Polling, sigh.) Console.Write("Waiting for systemd..."); int systemdPid; do { Thread.Sleep(500); systemdPid = Helpers.GetSystemdPid(); Console.Write("."); } while (systemdPid == 0); // Now that systemd exists, write out its (external) PID. // We do not need to store the inside-bottle PID anywhere for obvious reasons. // Create the path file. File.WriteAllText("/run/genie.systemd.pid", systemdPid.ToString()); // Wait for systemd to be in running state. int runningYet = 255; int timeout = Config.SystemdStartupTimeout; var ryArgs = new string[] { "-c", $"nsenter -t {systemdPid} -m -p systemctl is-system-running -q 2> /dev/null" }; do { Thread.Sleep(1000); runningYet = Helpers.RunAndWait("sh", ryArgs); Console.Write("!"); timeout--; if (timeout < 0) { // What state are we in? var state = Helpers.RunAndWaitForOutput("sh", new string[] { "-c", $"\"nsenter -t {systemdPid} -m -p systemctl is-system-running 2> /dev/null\"" }); if (state.StartsWith("starting")) { Console.WriteLine("\n\nSystemd is still starting. This may indicate a unit is being slow to start.\nAttempting to continue.\n"); } else { Console.WriteLine("\n\nTimed out waiting for systemd to enter running state.\nThis may indicate a systemd configuration error.\nAttempting to continue.\nFailed units will now be displayed (systemctl list-units --failed):"); Helpers.Chain("nsenter", new string[] { "-t", systemdPid.ToString(), "-m", "-p", "systemctl", "list-units", "--failed" }, "running command failed; nsenter for systemctl list-units --failed"); } break; } } while (runningYet != 0); Console.WriteLine(); }
// Add the "-wsl" suffix to the system hostname, and update the hosts file accordingly. internal static void UpdateHostname(bool verbose) { // Generate new hostname. if (verbose) { Console.WriteLine("genie: generating new hostname."); } string externalHost = HostHelpers.Hostname; if (verbose) { Console.WriteLine($"genie: external hostname is {externalHost}"); } // Make new hostname. string internalHost = $"{externalHost.Substring(0, (externalHost.Length <= 60 ? externalHost.Length : 60))}-wsl"; File.WriteAllLines("/run/hostname-wsl", new string[] { internalHost }); unsafe { var bytes = Encoding.UTF8.GetBytes("/run/hostname-wsl"); fixed(byte *buffer = bytes) { chmod(buffer, Convert.ToUInt16("644", 8)); } } // Hosts file: check for old host name; if there, remove it. if (verbose) { Console.WriteLine("genie: updating hosts file."); } try { var hosts = File.ReadAllLines("/etc/hosts"); var newHosts = new List <string> (hosts.Length); newHosts.Add($"127.0.0.1 localhost {internalHost}"); foreach (string s in hosts) { if (!( (s.Contains(externalHost) || s.Contains(internalHost)) && (s.Contains("127.0.0.1")) )) { newHosts.Add(s); } } File.WriteAllLines("/etc/hosts", newHosts.ToArray()); } catch (Exception ex) { Console.WriteLine($"genie: error updating host file: {ex.Message}"); Environment.Exit(130); } // Set the new hostname. if (verbose) { Console.WriteLine("genie: setting new hostname."); } if (!MountHelpers.BindMount("/run/hostname-wsl", "/etc/hostname")) { Console.WriteLine("genie: initializing bottle failed; bind mounting hostname"); Environment.Exit(EPERM); } }