forked from gcwnow/mininit
-
Notifications
You must be signed in to change notification settings - Fork 8
/
mininit.c
292 lines (247 loc) · 7.09 KB
/
mininit.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
/* SPDX-License-Identifier: BSD-2-Clause */
#define _DEFAULT_SOURCE
#define _GNU_SOURCE
#include <sys/mount.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#ifndef MS_MOVE
#define MS_MOVE 8192
#endif
#include "debug.h"
#include "loop.h"
#include "mininit.h"
#define ROOTFS_TYPE "squashfs"
#define ROOTFS_CURRENT "rootfs." ROOTFS_TYPE
#define ROOTFS_BACKUP ROOTFS_CURRENT ".bak"
#define ROOTFS_UPDATE "update_r.bin"
int create_mount_point(const char *path)
{
if (mkdir(path, 0755)) {
if (errno != EEXIST) {
WARNING("Failed to create '%s' mount point: %d\n", path, errno);
return -1;
}
}
return 0;
}
void perform_updates(bool is_backup)
{
bool boot_ro = access(".", W_OK) && errno == EROFS;
bool update_modules = !access("update_m.bin", R_OK);
bool update_rootfs = !access("update_r.bin", R_OK);
if (update_modules || update_rootfs) {
if (boot_ro) {
INFO("remounting boot device read-write\n");
if (mount(NULL, ".", NULL, MS_REMOUNT | MS_NOATIME, NULL)) {
ERROR("Unable to remount boot device read-write: %d\n", errno);
return;
}
boot_ro = false;
}
if (update_modules) {
INFO("performing modules update\n");
rename("modules.squashfs", "modules.squashfs.bak");
rename("modules.squashfs.sha1", "modules.squashfs.bak.sha1");
rename("update_m.bin", "modules.squashfs");
rename("update_m.bin.sha1", "modules.squashfs.sha1");
}
if (update_rootfs) {
INFO("performing rootfs update\n");
/* If rootfs_bak was not passed, or the backup is not available,
* make a backup of the current rootfs before the update */
if (!is_backup || access(ROOTFS_BACKUP, F_OK)) {
rename(ROOTFS_CURRENT, ROOTFS_BACKUP);
rename(ROOTFS_CURRENT ".sha1", ROOTFS_BACKUP ".sha1");
}
rename(ROOTFS_UPDATE, ROOTFS_CURRENT);
rename(ROOTFS_UPDATE ".sha1", ROOTFS_CURRENT ".sha1");
chown(ROOTFS_CURRENT, 0, 0);
chmod(ROOTFS_CURRENT, 0444);
}
sync();
}
if (!boot_ro) {
INFO("remounting boot device read-only\n");
if (mount(NULL, ".", NULL, MS_REMOUNT | MS_RDONLY, NULL)) {
ERROR("Unable to remount boot device read-only: %d\n", errno);
}
}
}
FILE *logfile;
char logbuf[LOG_BUF_SIZE];
int main(int argc, char **argv, char **envp)
{
struct stat stats;
logfile = stderr;
/* Create required mount points. */
create_mount_point("/dev");
create_mount_point("/root");
/* Mount devtmpfs to get a full set of device nodes. */
if (mount("devtmpfs", "/dev", "devtmpfs", 0, NULL) && errno != EBUSY) {
INFO("Couldn't mount devtmpfs on /dev: %d\n", errno);
/* If there are sufficient static device nodes in the fs containing
* mininit, we can boot without devtmpfs, so don't give up yet. */
}
/* Write our log messages to the kernel log. */
FILE *kmsg = fopen("/dev/kmsg", "w");
if (kmsg) {
#ifndef __KLIBC__
setlinebuf(kmsg);
#endif /* __KLIBC__ */
logfile = kmsg;
}
INFO("OpenDingux mininit 2.0.2\n");
if (!kmsg) {
WARNING("Failed to open '/dev/kmsg': %d\n", errno);
}
/* Look for "rootfs_bak" parameter. */
bool is_backup = false;
for (int i = 1; i < argc; i++) {
if (!strcmp(argv[i], "rootfs_bak")) {
is_backup = true;
break;
}
}
const char *boot_mount = mount_boot();
if (!boot_mount) {
return -1;
}
if (chdir(boot_mount)) {
ERROR("Unable to change to '%s' directory: %d\n", boot_mount, errno);
return -1;
}
perform_updates(is_backup);
/* Get free loop device. */
int devnr = logetfree();
if (devnr < 0) {
/* We're running early in the boot sequence, probably /dev/loop0
* is still available. */
devnr = 0;
}
char loop_dev[9 + 10 + 1];
sprintf(loop_dev, "/dev/loop%i", devnr);
/* Set the rootfs as the backing file for the loop device. */
const char *rootfs_img =
is_backup && !access(ROOTFS_BACKUP, F_OK)
? ROOTFS_BACKUP
: ROOTFS_CURRENT;
losetup(loop_dev, rootfs_img);
/* Mount the loop device that was just set up. */
DEBUG("Loop-mounting '%s' on '/root'\n", rootfs_img);
if (mount(loop_dev, "/root", ROOTFS_TYPE, MS_RDONLY, NULL)) {
ERROR("Failed to mount the rootfs image: %d\n", errno);
return -1;
}
INFO("%s mounted on /root\n", rootfs_img);
/* Check for /boot in the rootfs image */
if (stat("/root/boot", &stats)) {
ERROR("'boot' folder in the rootfs image is missing: %d\n", errno);
return -1;
}
if (!S_ISDIR(stats.st_mode)) {
ERROR("'boot' folder in the rootfs image is missing\n");
return -1;
}
/* Make the freshly mounted rootfs image the working directory. */
if (chdir("/root")) {
ERROR("Unable to change to '/root' directory: %d\n", errno);
return -1;
}
/* Move the devtmpfs mount to inside the rootfs tree. */
DEBUG("Moving '/dev' mount\n");
if (mount("/dev", "dev", NULL, MS_MOVE, NULL)) {
ERROR("Unable to move the '/dev' mount: %d\n", errno);
return -1;
}
/* Re-open the console device at the new location. */
int fd = open("dev/console", O_RDWR, 0);
if (fd < 0) {
ERROR("Unable to re-open console: %d\n", fd);
return -1;
}
if (dup2(fd, 0) != 0 || dup2(fd, 1) != 1 || dup2(fd, 2) != 2) {
ERROR("Unable to duplicate console handles\n");
return -1;
}
if (fd > 2) close(fd);
/* Open the old root while we can still access it. */
fd = open_dir_to_clean();
/* Now let's switch to the new root */
if (switch_root()) {
if (fd >= 0) close(fd);
return -1;
}
/* Make the freshly switched root the root of this process. */
if (chroot(".")) {
ERROR("'chroot' to new root failed: %d\n", errno);
if (fd >= 0) close(fd);
return -1;
}
/* And make it the working directory as well. */
if (chdir("/")) {
ERROR("'chdir' to new root failed: %d\n", errno);
if (fd >= 0) close(fd);
return -1;
}
INFO("root switch done\n");
/* Clean up the initramfs and then release it. */
if (fd >= 0) {
DEBUG("Removing initramfs contents\n");
const char *executable = argv[0];
while (*executable == '/') ++executable;
if (unlinkat(fd, executable, 0)) {
DEBUG("Failed to remove '%s' executable: %d\n", executable, errno);
}
if (unlinkat(fd, "dev/console", 0)) {
DEBUG("Failed to remove '/dev/console': %d\n", errno);
}
if (unlinkat(fd, "dev", AT_REMOVEDIR)) {
DEBUG("Failed to remove '/dev' directory: %d\n", errno);
}
if (unlinkat(fd, "boot", AT_REMOVEDIR)) {
DEBUG("Failed to remove '/boot' mount point: %d\n", errno);
}
if (unlinkat(fd, "root", AT_REMOVEDIR)) {
DEBUG("Failed to remove '/root' directory: %d\n", errno);
}
if (close(fd)) {
DEBUG("Failed to close initramfs: %d\n", errno);
}
}
/* Try to locate the init program. */
const char *inits[] = {
"/sbin/init",
"/etc/init",
"/bin/init",
"/bin/sh",
NULL,
};
for (int i = 0; ; i++) {
if (!inits[i]) {
ERROR("Unable to find the 'init' executable\n");
return -1;
}
DEBUG("Checking for 'init' executable: %s\n", inits[i]);
if (!access(inits[i], X_OK)) {
argv[0] = (char *)inits[i];
break;
}
}
INFO("starting %s\n", argv[0]);
if (kmsg) {
logfile = stderr;
fclose(kmsg);
}
/* Execute the 'init' executable */
execvpe(argv[0], argv, envp);
ERROR("Exec of 'init' failed: %d\n", errno);
return -1;
}