Пример #1
0
        // 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));
        }
Пример #2
0
        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));
        }
Пример #3
0
        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));
        }
Пример #4
0
        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));
        }
Пример #5
0
            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);
Пример #6
0
        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));
        }
Пример #7
0
            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));
            }
Пример #8
0
        // 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;
        }
Пример #9
0
        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));
        }
Пример #10
0
        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));
        }
Пример #11
0
        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));
        }
Пример #12
0
        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));
        }
Пример #13
0
        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));
        }
Пример #14
0
        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));
        }
Пример #15
0
        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));
        }
Пример #16
0
        // 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);
        }
Пример #17
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();
        }
Пример #18
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(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();
        }
Пример #19
0
        // 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);
            }
        }