Magellan Linux

Contents of /trunk/mkinitrd-magellan/klibc/usr/kinit/initrd.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1122 - (show annotations) (download)
Wed Aug 18 21:11:40 2010 UTC (13 years, 8 months ago) by niro
File MIME type: text/plain
File size: 4903 byte(s)
-updated to klibc-1.5.19
1 /*
2 * Handle initrd, thus putting the backwards into backwards compatible
3 */
4
5 #include <stdio.h>
6 #include <string.h>
7 #include <unistd.h>
8 #include <fcntl.h>
9 #include <errno.h>
10 #include <sys/stat.h>
11 #include <sys/mount.h>
12 #include <sys/ioctl.h>
13 #include <sys/wait.h>
14 #include "do_mounts.h"
15 #include "kinit.h"
16 #include "xpio.h"
17
18 #define BUF_SIZE 65536 /* Should be a power of 2 */
19
20 /*
21 * Copy the initrd to /dev/ram0, copy from the end to the beginning
22 * to avoid taking 2x the memory.
23 */
24 static int rd_copy_uncompressed(int ffd, int dfd)
25 {
26 char buffer[BUF_SIZE];
27 off_t bytes;
28 struct stat st;
29
30 dprintf("kinit: uncompressed initrd\n");
31
32 if (ffd < 0 || fstat(ffd, &st) || !S_ISREG(st.st_mode) ||
33 (bytes = st.st_size) == 0)
34 return -1;
35
36 while (bytes) {
37 ssize_t blocksize = ((bytes - 1) & (BUF_SIZE - 1)) + 1;
38 off_t offset = bytes - blocksize;
39
40 dprintf("kinit: copying %zd bytes at offset %llu\n",
41 blocksize, offset);
42
43 if (xpread(ffd, buffer, blocksize, offset) != blocksize ||
44 xpwrite(dfd, buffer, blocksize, offset) != blocksize)
45 return -1;
46
47 ftruncate(ffd, offset); /* Free up memory */
48 bytes = offset;
49 }
50 return 0;
51 }
52
53 static int rd_copy_image(const char *path)
54 {
55 int ffd = open(path, O_RDONLY);
56 int rv = -1;
57 unsigned char gzip_magic[2];
58
59 if (ffd < 0)
60 goto barf;
61
62 if (xpread(ffd, gzip_magic, 2, 0) == 2 &&
63 gzip_magic[0] == 037 && gzip_magic[1] == 0213) {
64 FILE *wfd = fopen("/dev/ram0", "w");
65 if (!wfd)
66 goto barf;
67 rv = load_ramdisk_compressed(path, wfd, 0);
68 fclose(wfd);
69 } else {
70 int dfd = open("/dev/ram0", O_WRONLY);
71 if (dfd < 0)
72 goto barf;
73 rv = rd_copy_uncompressed(ffd, dfd);
74 close(dfd);
75 }
76
77 barf:
78 if (ffd >= 0)
79 close(ffd);
80 return rv;
81 }
82
83 /*
84 * Run /linuxrc, for emulation of old-style initrd
85 */
86 static int run_linuxrc(int argc, char *argv[], dev_t root_dev)
87 {
88 int root_fd, old_fd;
89 pid_t pid;
90 long realroot = Root_RAM0;
91 const char *ramdisk_name = "/dev/ram0";
92 FILE *fp;
93
94 dprintf("kinit: mounting initrd\n");
95 mkdir("/root", 0700);
96 if (!mount_block(ramdisk_name, "/root", NULL, MS_VERBOSE, NULL))
97 return -errno;
98
99 /* Write the current "real root device" out to procfs */
100 dprintf("kinit: real_root_dev = %#x\n", root_dev);
101 fp = fopen("/proc/sys/kernel/real-root-dev", "w");
102 fprintf(fp, "%u", root_dev);
103 fclose(fp);
104
105 mkdir("/old", 0700);
106 root_fd = open_cloexec("/", O_RDONLY | O_DIRECTORY, 0);
107 old_fd = open_cloexec("/old", O_RDONLY | O_DIRECTORY, 0);
108
109 if (root_fd < 0 || old_fd < 0)
110 return -errno;
111
112 if (chdir("/root") ||
113 mount(".", "/", NULL, MS_MOVE, NULL) || chroot("."))
114 return -errno;
115
116 pid = vfork();
117 if (pid == 0) {
118 setsid();
119 /* Looks like linuxrc doesn't get the init environment
120 or parameters. Weird, but so is the whole linuxrc bit. */
121 execl("/linuxrc", "linuxrc", NULL);
122 _exit(255);
123 } else if (pid > 0) {
124 dprintf("kinit: Waiting for linuxrc to complete...\n");
125 while (waitpid(pid, NULL, 0) != pid) ;
126 dprintf("kinit: linuxrc done\n");
127 } else {
128 return -errno;
129 }
130
131 if (fchdir(old_fd) ||
132 mount("/", ".", NULL, MS_MOVE, NULL) ||
133 fchdir(root_fd) || chroot("."))
134 return -errno;
135
136 close(root_fd);
137 close(old_fd);
138
139 getintfile("/proc/sys/kernel/real-root-dev", &realroot);
140
141 /* If realroot is Root_RAM0, then the initrd did any necessary work */
142 if (realroot == Root_RAM0) {
143 if (mount("/old", "/root", NULL, MS_MOVE, NULL))
144 return -errno;
145 } else {
146 mount_root(argc, argv, (dev_t) realroot, NULL);
147
148 /* If /root/initrd exists, move the initrd there, otherwise discard */
149 if (!mount("/old", "/root/initrd", NULL, MS_MOVE, NULL)) {
150 /* We're good */
151 } else {
152 int olddev = open(ramdisk_name, O_RDWR);
153 umount2("/old", MNT_DETACH);
154 if (olddev < 0 ||
155 ioctl(olddev, BLKFLSBUF, (long)0) ||
156 close(olddev)) {
157 fprintf(stderr,
158 "%s: Cannot flush initrd contents\n",
159 progname);
160 }
161 }
162 }
163
164 rmdir("/old");
165 return 0;
166 }
167
168 int initrd_load(int argc, char *argv[], dev_t root_dev)
169 {
170 if (access("/initrd.image", R_OK))
171 return 0; /* No initrd */
172
173 dprintf("kinit: initrd found\n");
174
175 create_dev("/dev/ram0", Root_RAM0);
176
177 if (rd_copy_image("/initrd.image") || unlink("/initrd.image")) {
178 fprintf(stderr, "%s: initrd installation failed (too big?)\n",
179 progname);
180 return 0; /* Failed to copy initrd */
181 }
182
183 dprintf("kinit: initrd copied\n");
184
185 if (root_dev == Root_MULTI) {
186 dprintf("kinit: skipping linuxrc: incompatible with multiple roots\n");
187 /* Mounting initrd as ordinary root */
188 return 0;
189 }
190
191 if (root_dev != Root_RAM0) {
192 int err;
193 dprintf("kinit: running linuxrc\n");
194 err = run_linuxrc(argc, argv, root_dev);
195 if (err)
196 fprintf(stderr, "%s: running linuxrc: %s\n", progname,
197 strerror(-err));
198 return 1; /* initrd is root, or run_linuxrc took care of it */
199 } else {
200 dprintf("kinit: permament (or pivoting) initrd, not running linuxrc\n");
201 return 0; /* Mounting initrd as ordinary root */
202 }
203 }