20#include "pedigree/kernel/BootstrapInfo.h"
21#include "pedigree/kernel/LockGuard.h"
22#include "pedigree/kernel/Log.h"
23#include "pedigree/kernel/Service.h"
24#include "pedigree/kernel/ServiceFeatures.h"
25#include "pedigree/kernel/ServiceManager.h"
26#include "pedigree/kernel/core/BootIO.h"
27#include "pedigree/kernel/machine/Device.h"
28#include "pedigree/kernel/machine/Disk.h"
29#include "pedigree/kernel/machine/Machine.h"
30#include "pedigree/kernel/panic.h"
31#include "pedigree/kernel/process/Mutex.h"
32#include "pedigree/kernel/utilities/Iterator.h"
33#include "pedigree/kernel/utilities/List.h"
34#include "pedigree/kernel/utilities/Pointers.h"
35#include "pedigree/kernel/utilities/StaticString.h"
36#include "pedigree/kernel/utilities/String.h"
37#include "pedigree/kernel/utilities/StringView.h"
38#include "pedigree/kernel/utilities/Tree.h"
39#include "pedigree/kernel/utilities/Vector.h"
40#include "pedigree/kernel/utilities/utility.h"
42#include "modules/Module.h"
43#include "modules/system/lodisk/LoDisk.h"
45#include "modules/system/vfs/Filesystem.h"
46#include "modules/system/vfs/VFS.h"
48#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
49#include "pedigree/kernel/processor/hosted/smoke.h"
54static bool bRootMounted =
false;
56enum class RootSelectorKind { None, Uuid, Label };
57static RootSelectorKind g_RootSelectorKind = RootSelectorKind::None;
58static String g_RootSelectorValue;
61static Mutex g_MountLock;
63static FileDisk* g_pLiveDisk =
nullptr;
65static void error(
const char* s) {
70 bootIO.
write(str, BootIO::Red, BootIO::Black);
74static bool parseRootSelector() {
75 char* commandLine = g_pBootstrapInfo->getCommandLine();
80 RootSelectorKind kind = RootSelectorKind::None;
83 for (
auto argument : arguments) {
85 if (view.length() > 10 && view.
substring(0, 10) ==
"root=UUID=") {
86 kind = RootSelectorKind::Uuid;
87 value = view.
substring(10, view.length()).toString();
88 }
else if (view.length() > 11 && view.
substring(0, 11) ==
"root=LABEL=") {
89 if (kind != RootSelectorKind::Uuid) {
90 kind = RootSelectorKind::Label;
91 value = view.
substring(11, view.length()).toString();
96 if (kind == RootSelectorKind::None || !value.length()) {
101 if (!value.length()) {
105 g_RootSelectorKind = kind;
106 g_RootSelectorValue = value;
110static bool isRootFilesystem(
Filesystem* filesystem) {
111 if (g_RootSelectorKind == RootSelectorKind::Uuid) {
113 return filesystem->
getUuid(uuid) && uuid == g_RootSelectorValue;
115 if (g_RootSelectorKind == RootSelectorKind::Label) {
126 Disk* pDisk =
static_cast<Disk*
>(diskDevice);
127 for (
auto* filesystem : g_MountedFilesystems) {
128 if (filesystem->
getDisk() == pDisk) {
136 bool didMountAsRoot =
false;
138 if (!bRootMounted && isRootFilesystem(pFs)) {
139 NOTICE(
"Mounted " << stableName <<
" successfully as root.");
141 bRootMounted = didMountAsRoot =
true;
144 if (!didMountAsRoot) {
145 NOTICE(
"Mounted " << stableName <<
" at /media/" << stableName <<
".");
148 g_MountedFilesystems.pushBack(pFs);
157 if (!data || length !=
sizeof(
Device*)) {
169 for (
auto it = g_MountedFilesystems.begin(); it != g_MountedFilesystems.end();) {
172 while (ancestor && ancestor != device) {
184 NOTICE(
"Removed filesystem " << label);
188 it = g_MountedFilesystems.erase(it);
196#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
197static bool installHostedProfileRoot() {
199 if (!root || !root.get()->initialise(
nullptr))
202 const char* directories[] = {
"/etc",
"/dev",
"/dev/shm",
"/run",
"/run/lock",
"/run/sockets",
203 "/sys",
"/var",
"/var/run",
"/proc",
"/tmp"};
204 for (
const char* path : directories) {
205 if (!root.get()->createDirectory(
StringView(path), 0755))
211 const char* contents;
212 } accounts[] = {{
"/etc/passwd",
"root:x:0:0:root:/:/bin/sh\n"},
213 {
"/etc/group",
"root:x:0:root\n"}};
214 for (
const auto& account : accounts) {
215 if (!root.get()->createFile(
StringView(account.path), 0644))
218 File* file = root.get()->findRetained(
StringView(account.path), lease);
219 const size_t length = StringLength(account.contents);
220 if (!file || file->
write(0, length,
reinterpret_cast<uintptr_t
>(account.contents)) != length)
227 g_MountedFilesystems.pushBack(root.releaseOwnership());
235#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
236 const bool hostedProfile = hostedSyscallProfileRequested();
238 const bool hostedProfile =
false;
240 if (!hostedProfile && !parseRootSelector()) {
241 error(
"No valid root=UUID= or root=LABEL= selector was supplied.");
251 g_MountedFilesystems.pushBack(pRamFs);
259 pRuntimeFs->setProcessOwnership(
true);
261 g_MountedFilesystems.pushBack(pRuntimeFs);
263#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
265 if (!installHostedProfileRoot()) {
266 error(
"Unable to prepare the hosted syscall profiling root.");
278 ServiceManager::instance().
addService(
String(
"mountroot"), &g_MountService, &g_MountFeatures);
282 error(
"/media/raw does not exist - cannot continue startup.");
291 NOTICE(
"trying to find live disk");
293 if (pRamDisk && pRamDisk->initialise()) {
294 NOTICE(
"have a live disk");
296 g_pLiveDisk = pRamDisk;
300 bRootMounted =
false;
301 NOTICE(
"probing ram disk for partitions");
309 error(
"No root disk matched the supplied root filesystem selector.");
321 if (device == g_pLiveDisk) {
322 g_pLiveDisk =
nullptr;
328static bool isLiveDiskFilesystem(
Filesystem* filesystem) {
331 if (device == g_pLiveDisk) {
334 device = device->getParent();
339static void showUnmount(
Filesystem* filesystem) {
341 detail +=
"Unmounting ";
346static void destroy() {
349 NOTICE(
"Unmounting all filesystems...");
354 panic(
"mountroot could not drain the filesystem namespace");
356 for (
auto* filesystem : ownedBackings)
357 g_MountedFilesystems.pushBack(filesystem);
362 for (
auto filesystem : g_MountedFilesystems) {
363 if (g_pLiveDisk && isLiveDiskFilesystem(filesystem)) {
364 liveDiskFilesystems.
pushBack(filesystem);
366 backingFilesystems.
pushBack(filesystem);
372 while (liveDiskFilesystems.
count()) {
374 showUnmount(filesystem);
375 NOTICE(
"Unmounting " << filesystem->
getVolumeLabel() <<
" [" <<
Hex << filesystem <<
"]...");
376 if (!
VFS::instance().unregisterFilesystem(filesystem,
true,
true)) {
377 panic(
"mountroot could not cleanly unmount a live-disk filesystem");
379 NOTICE(
"unmount done");
386 panic(
"mountroot could not retire its live-disk device");
390 while (backingFilesystems.
count()) {
392 showUnmount(filesystem);
393 NOTICE(
"Unmounting " << filesystem->
getVolumeLabel() <<
" [" <<
Hex << filesystem <<
"]...");
394 if (!
VFS::instance().unregisterFilesystem(filesystem,
true,
true)) {
395 panic(
"mountroot could not cleanly unmount a backing filesystem");
397 NOTICE(
"unmount done");
400 g_MountedFilesystems.clear();
401 bRootMounted =
false;
403 NOTICE(
"Unmounting all filesystems has completed.");
406MODULE_INFO_RUNTIME_PINNED(
"mountroot", &init, &destroy,
"vfs",
"partition",
"rawfs",
"ramfs");
410MODULE_OPTIONAL_DEPENDS(
"filesystems",
"fat",
"ext2",
"iso9660",
"lodisk",
"diskimage");
EXPORTED_PUBLIC void write(T &str, Colour foreColour, Colour backColour)
static void addToRoot(Device *device)
static void foreach(Callback callback, Device *root=0)
@ Disk
A disk device - a block device in UNIX terms.
Device * getParent() const
virtual uint64_t write(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true) final
virtual const String & getVolumeLabel() const =0
virtual bool getUuid(String &uuid) const
static void setShutdownPhase(ShutdownPhase phase, const char *detail=nullptr)
bool serve(ServiceFeatures::Type type, void *data, size_t length) override
virtual bool initialise(Disk *pDisk) override
void addService(const String &serviceName, Service *s, ServiceFeatures *feats)
void removeService(const String &serviceName)
StringView substring(size_t start, size_t end, bool hashed=HASH_STRINGVIEWS_BY_DEFAULT) const
static UniquePointer< T > adopt(T *pointer)
bool setRootFilesystem(Filesystem *pFs)
String registerFilesystem(Filesystem *pFs, const String &preferredStableName)
Filesystem * getRootFilesystem() const
A vector / dynamic array.
void EXPORTED_PUBLIC panic(const char *msg) NORETURN
void pushBack(const T &value)